前言: 如果你經(jīng)常寫東西你就會發(fā)現(xiàn)宾添,寫的東西涉及原理和新知識增热、等需要主動學習的,閱讀量特別的低崎淳,閱讀量最高的基本是框架如何使用和使用中遇到哪些坑這種文章。我是不太喜歡去很深入的研究框架在項目中的用法愕把,但是沒辦法平時寫項目就的用到拣凹,只能硬著頭皮上了。
故事發(fā)生背景: 事情的起因是這樣的恨豁,我需要用 Antd table
的 filter
功能嚣镜,來動態(tài)控制展示列,到這都沒啥難的橘蜜,但是 filter 里面的數(shù)據(jù)是通過接口來獲得的菊匿,用戶操作動態(tài)列,點擊重置的時候要求不是清空所有的條件计福,而是回到初進頁面的狀態(tài)跌捆,即用戶沒操作之前的狀態(tài),即:可記住用戶行為。
于是難點就在這里了,根據(jù)官方給出的例子蕊肥,我無法辨別表格篩選條件為空的時候弄诲,是點擊重置按鈕觸發(fā)的還是全部取消之后點擊確認按鈕觸發(fā)的(本文核心要解決的問題)挚冤。這時候無路可選了,只能自己寫一個類似的功能,然后在使用 Antd table
「自定義篩選表頭功能」 集合到 table
上,讓我們開始吧钙姊。
一、create-react-app 使用 less 遇到的坑
用 React 越久 埂伦,我就越來越不喜歡它煞额,最新又用出問題了,我們知道 React 的插件基本沒有官方維護的沾谜,好處當然是遍地開花膊毁,但是壞也很明顯的,就是插件的兼容性對比 Vue 極差类早。
這不在使用 customize-cra 和 react-app-rewired
來定制化 webpack
配置 less
的時候媚媒,就遇到大坑嗜逻,customize-cra
支持 less-loader
的最高版本是 5
涩僻,我的少女心??,竟然落后現(xiàn)在兩個大版本。
二逆日、組件封裝 ??
完了嵌巷,demo 代碼我寫完,就去更新 使用 antd@4 table 自定義篩選表頭功能做一個聯(lián)動搜索表頭篩選 這篇文章了室抽,然后這篇文章就被扔下來了搪哪,就差邏輯說明沒寫了??。所以現(xiàn)在就相當于帶著你讀者們一起讀代碼 ??坪圾。
這是我公開發(fā)表的第一篇——使用 hooks
寫的 demo
晓折,寫的不好多見諒,順便說下我感覺它最大的缺點:因為閉包影響導致 state 出現(xiàn)的過時狀態(tài)兽泄,使 hooks 不能處理復雜邏輯漓概,還的用 class 組件,最大的好處就是性能控制相比類組件更加精細化了病梢。
封裝實現(xiàn)要求:和 Antd 官網(wǎng)
給出的 可控的篩選和排序 例子一樣胃珍。我們只增加全部取消之后點擊確定按鈕和點擊重置按鈕 ——按鈕事件觸發(fā)時的名字,就是打個標記蜓陌。
第一步:先給布局樣式 filterItem.less
文件:
.common-show-items{
.ant-popover-inner-content{
padding: 0 !important;
}
.ant-popover{
padding-top: 0;
.ant-popover-arrow{
display: none;
}
}
label{
display: inline-block;
width: 100%;
padding: 5px 12px;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
line-height: 22px;
white-space: nowrap;
cursor: pointer;
transition: all 0.3s;
&:hover{
background-color: #f5f5f5;
}
.ant-checkbox-checked .ant-checkbox-inner {
background-color: #256FEB;
border-color: #256FEB;
}
}
label.ant-checkbox-wrapper-checked{
color: #256FEB;
background-color: #e6f7ff;
}
.filter-dropdown-btns{
display: flex;
justify-content: space-between;
padding: 7px 8px 7px 3px;
overflow: hidden;
border-top: 1px solid #f0f0f0;
.ant-btn-primary{
background: #2A5FF1;
border-color: #2A5FF1;
}
button{
height: 24px;
padding: 0px 7px;
font-size: 14px;
border-radius: 2px;
}
}
}
第二步:filterItem.js
邏輯處理組件源代碼觅彰,已詳盡注釋:
import React, { useState, useCallback, useEffect } from "react";
import { Popover, Checkbox, Button } from "antd";
import "./filterItem.less";
const FilterItem = (props) => {
const { title, filters, isShowPop, popOverVisibleChange, okText, cancelText, handleChange, filterIcon, initItems = [] } = props;
const [ displayItems, setDisplayItems ] = useState(initItems);
const [ showItems, setShowItems ] = useState([]);
const [ onEvent, setOnEvent ] = useState(false);
// 圖標變色
const iconComponent = React.cloneElement(
filterIcon, {
style: { color: displayItems.length ? '#1890ff' : undefined }
}
);
/* 是否支持外部可控,如果外部可以打開關(guān)閉钮热,就不使用內(nèi)部的可控函數(shù) */
const [ filteredValue, setFilteredValue ] = useState(false);
const handleVisibleChange = useCallback(() => {
setFilteredValue(!filteredValue);
}, [ filteredValue ]);
/* 展示組件打開之后備份目前選中的展示項 => 用于點擊確定按鈕關(guān)閉的時候驗證選中項目是否發(fā)生改變 */
useEffect(() => {
// console.log(showItems, 99, isShowPop, filteredValue, onEvent);
if (isShowPop || filteredValue) {
if (onEvent) return;
setOnEvent(true);
setShowItems(displayItems);
} else {
setOnEvent(false);
};
}, [ isShowPop, filteredValue, onEvent, displayItems, showItems ]);
// 多選事件
const handleFilterChange = useCallback(e => {
setDisplayItems(e);
}, [ ]);
// 展示checkbox
const checkboxGroup = useCallback(() => {
return filters.map(({ text, value }) => <div key={value}><Checkbox value={value} >{text}</Checkbox></div>);
}, [ filters ]);
/*
核心功能函數(shù)
1. 展示組件打開之后填抬,無任何操作,直接點擊確定按鈕關(guān)閉,不觸發(fā)事件
2. 展示組件打開之后隧期,無任何操作痴奏,直接點擊重置按鈕關(guān)閉,不觸發(fā)事件
*/
const handleItemsEquality = useCallback((displayItems, showOrInitItems, close) => {
let count = 0, maxSizeItem, minSizeItem;
/*
判斷兩個展示項完全相等邏輯:
判斷操作的展示項和備份的展示項 及 操作的展示項和初始化的展示項
找出兩者之間最長的,用最長的那個去遍歷短的厌秒,相等就計數(shù)读拆,最后查看是否完全相等
*/
if (displayItems.length > showOrInitItems.length) {
maxSizeItem = displayItems;
minSizeItem = showOrInitItems;
} else {
maxSizeItem = showOrInitItems;
minSizeItem = displayItems;
};
maxSizeItem.forEach(item => {
if (minSizeItem.includes(item)) count++;
});
if (count === maxSizeItem.length){
// 相等,直接關(guān)閉鸵闪,返回 true 不觸發(fā)回調(diào)
close();
return true;
} else {
return false;
};
}, [ ]);
const handleFilterBtns = useCallback(eventName => {
const close = popOverVisibleChange ?? handleVisibleChange;
if (eventName === "OK") {
const isCanClick = handleItemsEquality(displayItems, showItems, close);
if (isCanClick) return;
handleChange({ action: eventName, currentShowItem: displayItems, confirm: close });
} else {
const isCanClick = handleItemsEquality(displayItems, initItems, close);
if (isCanClick) return;
setDisplayItems(initItems);
setShowItems(initItems);
handleChange({ action: eventName, currentShowItem: initItems, confirm: close });
};
}, [ displayItems, showItems, initItems, handleChange, handleItemsEquality, handleVisibleChange, popOverVisibleChange ]);
return (
<>
<div className="common-show-items">
<Popover
trigger="click"
placement="bottomLeft"
visible={isShowPop ?? filteredValue}
getPopupContainer={(Node) => Node.parentNode}
onVisibleChange={popOverVisibleChange ?? handleVisibleChange}
content={
<div className="filter-group">
<Checkbox.Group onChange={handleFilterChange} value={displayItems}>
{checkboxGroup()}
</Checkbox.Group>
<div className="filter-dropdown-btns">
<Button type="link" onClick={handleFilterBtns.bind({}, "Reset")}>{cancelText ?? "重置"}</Button>
<Button type="primary" onClick={handleFilterBtns.bind({}, "OK")}>{okText ?? "確定"}</Button>
</div>
</div>
} >
<Button>{iconComponent ?? null}{title}</Button>
</Popover>
</div>
</>
);
};
export default FilterItem;
第三步:如何在項目中使用:
import React, { useState, useCallback } from 'react';
import FilterItem from './view/filterItem';
import { SettingOutlined } from '@ant-design/icons';
import './App.css';
function App() {
/* 1. 外部也可以控制展開收起 */
// const [ isShowPop, setShowPop ] = useState(false);
// const popOverVisibleChange = useCallback(() => {
// setShowPop(!isShowPop);
// }, [ isShowPop ]);
const handleChange = useCallback(({ action, currentShowItem, confirm }) => {
confirm();
console.log("handleChange", action, currentShowItem);
}, [ ]);
return (
<div className="App">
<FilterItem
// isShowPop={isShowPop}
// popOverVisibleChange={popOverVisibleChange}
// 2. 使用 okText 和 cancelText 自定義按鈕文字
// okText="OK"
// cancelText="Reset"
// 確認按鈕和重置按鈕正確觸發(fā)的回調(diào)函數(shù)
handleChange={handleChange}
// 展示項的名字
title={"列展示"}
// 展示項的圖標
filterIcon={<SettingOutlined />}
// 默認顯示的展示項
initItems={["London"]}
// 展示項列表
filters={[
{
text: '倫敦',
value: 'London',
},
{
text: '紐約',
value: 'New York',
},
{
text: '香港',
value: 'Hong Kong',
},
{
text: '阿拉斯加',
value: 'Alaska',
},
{
text: '東京',
value: 'Tōkyō',
},
{
text: '上海',
value: 'shanghai',
}
]}
/>
</div>
);
}
export default App;
第四步:動圖演示
動圖演示 filterItems
組件表現(xiàn)出的功能:
- 初始化展示項檐晕。
- 展開,點擊增加選中項蚌讼,點擊確定按鈕辟灰,觸發(fā)回調(diào)事件,扔出結(jié)果篡石。
- 展開芥喇,點擊減少選中項,點擊確定按鈕凰萨,觸發(fā)回調(diào)事件继控,扔出結(jié)果械馆。
- 展開,先點擊減少選中項武通,再點擊增加選中項霹崎,需要保持打開時的選中項,最后點擊確定按鈕冶忱,觸發(fā)回調(diào)事件尾菇,扔出結(jié)果。
- 展開囚枪,無操作點擊確定按鈕派诬,不觸發(fā)任何事件。
- 展開链沼,無操作點擊重置按鈕千埃,不觸發(fā)任何事件。
- 點擊空白區(qū)域相當于點擊確定
三忆植、集成到 Antd table 上
太簡單了放可,不寫了,自己琢磨去吧朝刊。
寫作時間 Sunday, September 20, 2020 00:07:38
昨天比現(xiàn)在早一個小時耀里,Vue 3.0 正式版發(fā)布了,就問問你啥感受??????