April28其他总结


April 28 其他总结

检测是否是多维数组
 const isDeep = arr.some(item=> item instanceof Array) // 检测是否是多维数组   
正则匹配
 /^[\u4e00-\u9fa5_a-zA-Z0-9(?=.*,)]{1,10}$/  

// 匹配数字、字母、汉字、逗号零个或多个(可选),1 ~ 10 个字符
useRef 使用时的一些问题 子组件ref
export interface Mutable {
    getFormValue: () => void;
    resetForm: () => void;
}

const hotRef: MutableRefObject<any> = useRef({} as Mutable);  
 // 报错: _hotRef$current.resetForm is not a function 
const hotRef: MutableRefObject<any> = useRef(null);  // 不报错
useRef 使用时的一些问题2 值的保存
// useRef 类型判断、配合子组件forwordref时的类型 、用 useRef保存值,初始值不要给null    

const query: MutableRefObject<any> = useRef(null); // 错误   
const query: MutableRefObject<any> = useRef({});  // 正确

// 待验证
antd 的一些组件在绑定初始value为undefined时才显示placehloder的问题
// 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全新的数据请求方式 参数拼接的方式
// post全新的数据请求方式 参数拼接的方式

export const changeDataStatus = async (params: any) => {
    const {materialCategory, materialIds, materialStatus} = params; // 全部拼接为string
    const result = await ajax.post(
        capi.setDataStatus +
            '?materialCategory=' +
            `${materialCategory}` +
            '&materialIds=' +
            `${materialIds.join()}` +
            '&materialStatus=' +
            `${materialStatus}`
    );
    return result.status;
};
table 选中项
// 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新的使用方式
// redux新的使用方式 hotSearch.ts

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; // 组件外

// 在组件内触发, 任意一个func里

  store.dispatch(
      saveTabelData({
          rows,
          count,
      })
  );

// 使用redux state 

  const HotSearchState = useSelector<RootState, Pick<HotSearchState, 'rows' | 'count' | 'page' | 'pageSize'>>(state =>
      _.pick(state.hotSearch, ['rows', 'count', 'page', 'pageSize'])
  );
  const {rows, count, page, pageSize} = HotSearchState;

// 这些解构的值可以直接当做变量使用
// App.tsx
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
// 封装 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)}
        />
    );

    /* 
    * 这样封装的column 可以使用传进来的方法,
    *
    * 并向传入的方法传递及时的值 (important!)
    * 
    * 外部传入的这些函数用这些及时的值去做一些事。
    */

    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);
};

/**
*
* list switch 前端更新 于同步后端接口
*/
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);
    })();
};

/*
*
* 查看详情时请求接口,
* 
* 根据不同flag 确定不同组件请求不同的接口
*
*/
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); // 会返回column数组

const tableConfig = { // 将 tableConfig 解构于 table 标签
    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;

/**
*
* 由于不同的flag都会进入 Materiel 组件,
*
* 虽然都使用的是统一组件,
* 
* 但是flag不同,数据也就不同,
* 
* 用flag 区分数据就好了
* 
* 在使用redux时,用不同的 action 保存不同组件的数据 就可以避免数据混乱。
*
* 虽然使用的同一组件,
*
* 但数据不同,那他们就是有区别的,
*  
* 即使这两个组件本身相差不大,
*
* 也会因为flag 的不同调用不同的接口和func
*
*/
前端更新的思想及其好处
在视图发生变化时,不调用接口似的视图更新,

而是采用前端更新的方式来更新视图,

同时同步数据库数据,

在下一次刷新页面时调用接口,

这样的操作一定程度上优化了性能,

减轻了服务器压力,

是性能优化的可选措施。
table rowSelection 不指定唯一key的后果
如果要选中多行在 不指定  rowKey: (record: any) => record.nid 时,

选中一个就会全部选中,这是因为key重复或者没有指定key!
antd tooltip 文字hover显示
// 使用 Tooltip 包裹要hover显示气泡的元素                   
<Tooltip title={tag} key={tag}>
      <Tag color='blue'>
          <div
              style={{
                  overflow: 'hidden',
                  textOverflow: 'ellipsis',
                  whiteSpace: 'nowrap',
                  cursor: 'pointer',
              }}
          >
              {tag}
          </div>
      </Tag>
  </Tooltip>

文章作者: KarlFranz
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 reprint policy. If reproduced, please indicate source KarlFranz !
评论
  目录