April 28 其他总结
检测是否是多维数组
const isDeep = arr.some(item=> item instanceof Array)
正则匹配
/^[\u4e00-\u9fa5_a-zA-Z0-9(?=.*,)]{1,10}$/
useRef 使用时的一些问题 子组件ref
export interface Mutable {
getFormValue: () => void;
resetForm: () => void;
}
const hotRef: MutableRefObject<any> = useRef({} as Mutable);
const hotRef: MutableRefObject<any> = useRef(null);
useRef 使用时的一些问题2 值的保存
const query: MutableRefObject<any> = useRef(null);
const query: MutableRefObject<any> = useRef({});
antd 的一些组件在绑定初始value为undefined时才显示placehloder的问题
select 和 caster 组件,初始值给 '' 不能显示placeholder, 但给undefined就可以显示?
数组类型
Array<string | number> === string[] | number[]
一个函数管理多个列表状态
import {useState, useEffect, useMemo, useCallback} from 'react';
import {Card, Space, Button} from 'antd';
import {SingleValueType} from 'rc-cascader/lib/Cascader';
import cn from 'classnames';
import styles from './index.less';
interface Region {
region: string;
regionCode: string;
}
const ZERO = 0;
const initBelongList = [0, 1, 2];
const QueryList = (props: any) => {
const threeList = = [
{
name: '热搜主榜',
key: '1',
queryLisy: [
{
text: '热搜1',
nums: 3234,
key: 0,
},
]
},
...
..
];
const {list, num} = props?.query.current;
const [curIndex, setCurIndex] = useState<number[]>(initBelongList);
const [cardList, setCardList] = useState(threeList);
...
..
const handelClick = useCallback(
(belongIndex: number, queryIndex: number) => {
setCurIndex(
curIndex.map((item, ind: number) => {
if (ind === belongIndex) {
const curItem = queryIndex;
return curItem;
}
return item;
})
);
},
[curIndex]
);
useEffect(() => {
(() => {
if (list && list[0] && num) {
setCardList(state => {
return state.map((item: any, index: number) => {
if (list[index].swiVal === ZERO) {
item.name = list[index].inpVal;
item.queryLisy = item.queryLisy.slice(0, num);
return item;
}
item.queryLisy = item.queryLisy.slice(0, num);
return item;
});
});
}
})();
}, [list, num]);
return (
<div className={styles.wrap}>
{renderSearch()}
<div className={styles.cardList}>
{useMemo(() => {
return cardList.map((item: any, ind: number) => {
const {name, key, queryLisy} = item;
return (
<Card className={styles.card} key={key} title={name} bordered={false}>
{queryLisy.map((ele: any, index: number) => {
const {text, nums} = ele;
const chooseIndex = styles.chooseIndex;
return (
<div
key={ele.key}
className={cn(styles.listItem, {[chooseIndex]: curIndex[ind] === index})}
onClick={() => handelClick(ind, index)}
>
<Space size={'large'}>
<span className={styles.radios}>{index + 1}</span>
<span>{text}</span>
</Space>
<span>{nums}</span>
</div>
);
})}
</Card>
);
});
}, [cardList, curIndex, handelClick])}
</div>
</div>
);
};
export default QueryList;
post全新的数据请求方式 参数拼接的方式
export const changeDataStatus = async (params: any) => {
const {materialCategory, materialIds, materialStatus} = params;
const result = await ajax.post(
capi.setDataStatus +
'?materialCategory=' +
`${materialCategory}` +
'&materialIds=' +
`${materialIds.join()}` +
'&materialStatus=' +
`${materialStatus}`
);
return result.status;
};
table 选中项
export type RowKeys = Array<string | number>;
....
...
const [selectedRowKeys, setSelectedRowKeys] = useState<RowKeys>([]);
const [tableRows, setTableRows] = useState<any[]>([]);
const rowSelection = {
selectedRowKeys,
onChange: onSelectChange,
};
const tableConfig = {
rowSelection: rowSelection,
columns: columns,
dataSource: rows,
rowKey: (record: any) => record.hotSearchId,
};
const onSelectChange = (selectedRowKeys: RowKeys, selectedRows: any) => {
setSelectedRowKeys(selectedRowKeys);
setTableRows(selectedRows);
};
<Tabel {...tableConfig} />
...
..
.
redux新的使用方式
import {createSlice} from '@reduxjs/toolkit';
import {RootState} from '.';
export interface HotSearchState {
rows: any[];
count: number;
page?: number;
pageSize?: number;
}
const initialState: HotSearchState = {
rows: [],
count: 0,
page: 1,
pageSize: 10,
};
export const hotSearchSlice = createSlice({
name: 'hotSearch',
initialState,
reducers: {
saveTabelData: (state, {payload}: {payload: HotSearchState}) => {
const {rows, count, page, pageSize} = payload;
state.rows = rows;
state.count = count;
state.page = page;
state.pageSize = pageSize;
},
},
});
export const selectHotSearch = (state: RootState) => state.hotSearch;
export default hotSearchSlice.reducer;
import {configureStore, ThunkAction, Action, combineReducers} from '@reduxjs/toolkit';
import commonReducer from './common';
import hotSearchReducer from './hotSearch';
import dataReducer from './data';
import statiSticsReducer from './statistics';
const rootReducer = combineReducers({
common: commonReducer,
hotSearch: hotSearchReducer,
data: dataReducer,
statistics: statiSticsReducer,
});
const store = configureStore({
reducer: rootReducer,
});
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, RootState, unknown, Action<string>>;
export default store;
import {HotSearchState, hotSearchSlice} from '@/store/hotSearch';
import store from '@/store/index';
const {saveTabelData} = hotSearchSlice.actions;
store.dispatch(
saveTabelData({
rows,
count,
})
);
const HotSearchState = useSelector<RootState, Pick<HotSearchState, 'rows' | 'count' | 'page' | 'pageSize'>>(state =>
_.pick(state.hotSearch, ['rows', 'count', 'page', 'pageSize'])
);
const {rows, count, page, pageSize} = HotSearchState;
import {BrowserRouter as Router} from 'react-router-dom';
import {Provider} from 'react-redux';
import store from './store';
import Layout from './Layout/BasicLayout';
export default function App() {
return (
<Provider store={store}>
<Router basename='/yl'>
<Layout />
</Router>
</Provider>
);
}
封装 table column
import {Switch} from 'antd';
import moment from 'moment';
import findCode from '@/page/components/utils/findCode';
import {mapSource} from './mapSource';
interface ColumnType {
config: any;
fnc: (item: any) => void;
listFun: (_: any, index: number, val: boolean) => void;
lookDetail: (item: any) => void;
}
const SOURCE_TYPE = '数据源类别';
const column = (props: ColumnType) => {
const {config, fnc, listFun, lookDetail} = props;
const {name, source, type, id, time, status, todo} = config;
const renderSwitch = (record: any, index: number) => (
<Switch
size='default'
checkedChildren='开'
unCheckedChildren='关'
defaultChecked
checked={record.bd_status}
onChange={e => listFun(record, index, e)}
/>
);
const handelCreate = (record: any) => {
fnc(record);
};
const handelDetail = (record: any) => {
lookDetail(record);
};
const columns = [
{
title: name,
dataIndex: 'title',
},
{
title: type,
dataIndex: 'baidu_cate',
render: (baidu_cate: string, record: any) => {
return <span>{type === SOURCE_TYPE ? mapSource[baidu_cate] : findCode(record.city_code)}</span>;
},
},
{
title: id,
dataIndex: 'nid',
},
{
title: time,
dataIndex: 'update_time',
render: (_: string, record: any) => {
return <span>{moment(record.update_time).format('YYYY / MM / DD')}</span>;
},
},
{
title: status,
dataIndex: 'bd_status',
render: (_: string, record: any, index: any) => {
return renderSwitch(record, index);
},
},
{
title: todo,
dataIndex: 'todo',
render: (_: string, record: any) => {
return (
<div style={{color: '#2056da', cursor: 'pointer'}}>
<span style={{marginRight: '15px'}} onClick={() => handelDetail(record)}>
查看详情
</span>
<span onClick={() => handelCreate(record)}>创建白名单</span>
</div>
);
},
},
];
source &&
columns.splice(2, 0, {
title: source,
dataIndex: 'brand_name',
});
return columns;
};
export default column;
import column from './Column';
const COLUMN_CONFIG = flag === DOOR ? TYPE_TWO : TYPE_ONE;
const openModal = (item: any) => {
setModalFlag(WHITE);
setWhite(item);
setVisit(true);
};
const listChange = (record: any, index: number, val: boolean) => {
const num = val === true ? ONE : ZERO;
const newRows = rows.map((item: any, ind: number) => {
if (ind === index) {
const newItem = _.cloneDeep(item);
newItem.bd_status = num;
return newItem;
}
return item;
});
store.dispatch(
actionFunc({
rows: newRows,
count,
})
);
(async () => {
const params = {
materialCategory: record.baidu_cate,
materialIds: [record.nid],
materialStatus: record.bd_status,
};
await apiFunc(params);
})();
};
const lookDetail = (item: any) => {
setModalFlag(DETAIL);
setVisit(true);
const params = {
materialCategory: item.baidu_cate,
materialId: item.nid,
};
if (flag === DATA) {
(async () => {
const res = await getDetail(params);
setDetailData(res);
})();
}
if (flag === DOOR) {
(async () => {
const res = await getDoorDetail(params);
setDetailData(res);
})();
}
};
const columnConfig = {
config: COLUMN_CONFIG,
fnc: openModal,
listFun: listChange,
lookDetail,
};
const commonColumn = column(columnConfig);
const tableConfig = {
rowSelection: rowSelection,
columns: commonColumn,
dataSource: rows,
rowKey: (record: any) => record.nid,
};
项目两个极其相似的组件的样式以及逻辑复用
import React, {FC} from 'react';
import CTabs from '@/components/CTabs';
import Materiel from './components/Materiel';
import styles from './index.less';
const TEXT_CONFIG = {
content: '物料名称:',
type: '数据源类别:',
status: '物料状态:',
time: '起止时间:',
name: '',
flag: 'data',
};
const TEXT_CONFIG_ONE = {
content: '物料名称:',
type: '门店区域:',
status: '物料状态:',
time: '起止时间:',
name: '品牌名称:',
flag: 'door',
};
const cTabs = [
{
index: '1',
tab: '物料查看',
cont: <Materiel TEXT_CONFIG={TEXT_CONFIG} />,
},
{
index: '2',
tab: '门店管理',
cont: <Materiel TEXT_CONFIG={TEXT_CONFIG_ONE} />,
},
];
const Data: FC = () => {
return (
<div className={styles.dataWrap}>
<CTabs tabs={cTabs} />
</div>
);
};
export default Data;
前端更新的思想及其好处
在视图发生变化时,不调用接口似的视图更新,
而是采用前端更新的方式来更新视图,
同时同步数据库数据,
在下一次刷新页面时调用接口,
这样的操作一定程度上优化了性能,
减轻了服务器压力,
是性能优化的可选措施。
table rowSelection 不指定唯一key的后果
如果要选中多行在 不指定 rowKey: (record: any) => record.nid 时,
选中一个就会全部选中,这是因为key重复或者没有指定key!
<Tooltip title={tag} key={tag}>
<Tag color='blue'>
<div
style={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
cursor: 'pointer',
}}
>
{tag}
</div>
</Tag>
</Tooltip>