• React封装强业务hook的一个例子


    最近因为使用列表展示的需求有点多,就想着把列表分页筛选的逻辑抽象一下。看了umi的一个useTable的hook,也不能满足业务需要,于是就自己写了一个,支持本地分页筛选和接口分页筛选。

    思路就是,筛选的字段都使用form表单控制,然后在hook里面将form和table联合起来。

    下面贴出源码 

    useFormTable.tsx  

    import { TableProps, PaginationProps } from '@slardar/antd';
    import react, { useEffect } from 'react';
    import {
    PaginationConfig,
    SorterResult,
    TableCurrentDataSource
    } from 'antd/lib/table';
    import { useDeepCompareEffect } from '@byted-woody/slardar';

    export type WrappedFormUtils = any;

    export interface TableControlState<T> extends TableProps<DataRow> {
    pagination: PaginationProps;
    sorter?: SorterResult<DataRow>;
    loading?: boolean;
    }

    // 搜索参数描述
    export interface SearchDescribe {
    // 字段名
    fieldName: string;
    iniValue?: any;
    // 输入值解析,比如日期输入解析
    decodeFn?: any;
    // 自定义搜索函数
    searchFn?: (record: DataRow, desc: SearchDescribe) => boolean;
    // 解析后的值
    searchValue?: any;
    // 调用接口或者只在本地过滤
    searchMod?: 'api' | 'local';
    // 搜索的字段,默认只搜索当前字段
    searchFields?: string[];
    }

    export interface DataReceive {
    pageSize?: number;
    pageIndex?: number;
    pageTotal?: number;
    pageData: any[];
    }

    export type DataRow = { [key: string]: any };
    export type DataApiGet = (
    apiParams,
    pagination?: { pageIndex?: number; pageSize?: number }
    ) => Promise<DataReceive>;

    export interface FormTableReq {
    form: WrappedFormUtils;
    getDataApi: DataApiGet;
    getDataParam?: { [key: string]: any };
    // 表单的字段解析
    includeFormFields?: (SearchDescribe | string)[];
    // 本地分页
    localPagination?: boolean;
    // 本地搜索
    localSearch?: boolean;
    // 本地分页+搜索
    local?: boolean;
    afterFetchData?: (v: any) => void;
    validateParam?: (param: any) => boolean;
    }

    const defaultTableState: TableControlState<DataRow> = {
    pagination: { current: 1, total: 0, pageSize: 10 },
    dataSource: [],
    loading: true
    };

    export type FormTableRet = [
    TableControlState<DataRow>,
    { fetchData: () => void }
    ];

    export function useFormTable(options: FormTableReq): FormTableRet {
    if (options.local) {
    options?.includeFormFields?.forEach(d => (d.searchMod = 'local'));
    return useFormTableLocal(options);
    } else {
    return useFormTableDB(options);
    }
    }

    // 本地分页筛选版本
    export function useFormTableLocal(options: FormTableReq): FormTableRet {
    let { form, getDataApi, includeFormFields } = options;
    let currentFormValue = form.getFieldsValue();
    // 缓存数据
    let cacheDataListRef = React.useRef<DataRow[]>([]);
    let [tableState, setTableState] = React.useState<TableControlState<DataRow>>(
    defaultTableState
    );
    let searchApiParam = {};
    let searchLocalParam: SearchDescribe[] = [];
    if (Array.isArray(includeFormFields)) {
    includeFormFields?.forEach(describe => {
    if (typeof describe === 'string') {
    let value = currentFormValue[describe];
    searchApiParam[describe] = value;
    } else {
    let value = currentFormValue[describe.fieldName];
    if (describe.decodeFn) {
    value = describe.decodeFn(value);
    }
    if (describe.searchMod === 'api') {
    searchApiParam[describe.fieldName] = value;
    } else {
    searchLocalParam.push(
    Object.assign({ searchValue: value }, describe)
    );
    }
    }
    });
    } else {
    searchApiParam = currentFormValue;
    }

    function getTableApiData() {
    getDataApi(searchApiParam).then(data => {
    cacheDataListRef.current = data.pageData;
    setTableState(prevState => {
    return Object.assign({}, prevState, { dataSource: [] });
    });
    });
    }

    useEffect(getTableApiData, []);

    let { data, total } = calculatePageData(
    tableState,
    cacheDataListRef.current,
    searchLocalParam
    );

    function onSorterChange(
    _pagination: PaginationConfig,
    _filters: Record<keyof DataRow, string[]>,
    _sorter: SorterResult<DataRow>,
    _extra: TableCurrentDataSource<DataRow>
    ) {
    setTableState(prevState => {
    return Object.assign({}, prevState, { sorter: _sorter });
    });
    }

    let newPage: PaginationProps = {
    total: total,
    onChange: (page, pageSize) => {
    setTableState(prevState => {
    prevState.pagination.pageSize = pageSize;
    prevState.pagination.current = page;
    return Object.assign({}, prevState);
    });
    }
    };

    let finalPagination: PaginationProps = Object.assign(
    {},
    tableState.pagination,
    newPage
    );

    return [
    { pagination: finalPagination, dataSource: data, onChange: onSorterChange },
    { fetchData: getTableApiData }
    ];
    }

    // 接口分页筛选版本 待完善
    export function useFormTableDB(options: FormTableReq): FormTableRet {
    let { form, getDataApi, includeFormFields } = options;
    let currentFormValue = form.getFieldsValue();
    let [state, setState] = React.useState<TableControlState<DataRow>>(
    defaultTableState
    );
    let searchApiParam: { [key: string]: any } = {};
    let onceRef = React.useRef(false);
    // 计算接口参数
    if (Array.isArray(includeFormFields)) {
    includeFormFields?.forEach(describe => {
    if (typeof describe === 'string') {
    let value = currentFormValue[describe];
    searchApiParam[describe] = value;
    } else {
    let value = currentFormValue[describe.fieldName];
    if (!onceRef.current && describe.iniValue) {
    value = describe.iniValue;
    }
    if (describe.decodeFn) {
    value = describe.decodeFn(value);
    Object.assign(searchApiParam, value);
    } else {
    searchApiParam[describe.fieldName] = value;
    }
    }
    });
    } else {
    searchApiParam = currentFormValue;
    }
    Object.assign(searchApiParam, options.getDataParam);
    const pageParam = {
    pageIndex: state.pagination.current,
    pageSize: state.pagination.pageSize
    };

    function getTableApiData() {
    if (options.validateParam && !options.validateParam(searchApiParam)) {
    return;
    }
    setState(prevState => {
    return Object.assign({}, prevState, {
    loading: true
    } as TableControlState<any>);
    });
    getDataApi(searchApiParam, pageParam).then(data => {
    const { pageData, pageTotal } = data;
    onceRef.current = true;
    setState(prevState => {
    return Object.assign({}, prevState, {
    dataSource: pageData,
    pagination: {
    current: pageParam.pageIndex,
    total: pageTotal || 0,
    pageSize: pageParam.pageSize
    },
    loading: false
    } as TableControlState<any>);
    });
    // 将表单数据同步到query
    if (options.afterFetchData) {
    options.afterFetchData(currentFormValue);
    }
    });
    }
    useDeepCompareEffect(getTableApiData, [searchApiParam, pageParam]);

    function onSorterChange(
    _pagination: PaginationConfig,
    _filters: Record<keyof DataRow, string[]>,
    _sorter: SorterResult<DataRow>,
    _extra: TableCurrentDataSource<DataRow>
    ) {
    setState(prevState => {
    return Object.assign({}, prevState, { sorter: _sorter });
    });
    }

    let finalPagination: PaginationProps = Object.assign(
    {
    total: state.pagination.total,
    onChange: (page, pageSize) => {
    setState(prevState => {
    prevState.pagination.pageSize = pageSize;
    prevState.pagination.current = page;
    return Object.assign({}, prevState);
    });
    }
    },
    state.pagination
    );
    let dataSource = state.dataSource;
    if (options.localPagination) {
    let { data, total } = calculatePageData(state, state.dataSource as any, []);
    finalPagination.total = total;
    dataSource = data;
    }

    return [
    {
    pagination: finalPagination,
    dataSource: dataSource,
    onChange: onSorterChange,
    loading: state.loading
    },
    { fetchData: getTableApiData },
    state
    ] as any;
    }

    // 排序,筛选,计算分页数据
    function calculatePageData(
    state: TableControlState<DataRow>,
    dataList: DataRow[],
    param: SearchDescribe[]
    ) {
    let { pagination, sorter } = state;
    let { current = 1, pageSize = 10 } = pagination;
    let copyDataList = Array.from(dataList);
    // 排序
    if (sorter?.column) {
    let order = sorter.order;
    let sortField = sorter.columnKey;
    copyDataList = copyDataList.sort((a, b) => {
    if (order === 'ascend') {
    return a[sortField] - b[sortField];
    } else {
    return b[sortField] - a[sortField];
    }
    });
    }
    // 筛选
    if (Array.isArray(param) && param.length > 0) {
    copyDataList = copyDataList.filter(function filter(v) {
    return param.every(desc => {
    let { fieldName, searchValue, searchFields, searchFn } = desc;
    let fieldValue = v[fieldName];
    let searchString = searchValue;
    if (!searchString) {
    return true;
    }
    if (searchFn) {
    return searchFn(v, desc);
    }
    if (
    typeof fieldValue !== 'string' ||
    typeof searchString !== 'string'
    ) {
    return true;
    }
    if (searchFields?.length) {
    return searchFields?.some(fieldName => {
    let value = v[fieldName];
    if (typeof value === 'string') {
    value.includes(searchString);
    }
    return false;
    });
    } else {
    return fieldValue.includes(searchString);
    }
    });
    });
    }
    // 分页
    let displayData = copyDataList.slice(
    (current - 1) * pageSize,
    current * pageSize
    );
    // 默认空数据的展示
    displayData.forEach(d => {
    Object.entries(d).forEach(([k, v]) => {
    if (v !== 0 && !v) {
    d[k] = '---';
    }
    });
    return d;
    });
    return { data: displayData, total: copyDataList.length };
    }

    vi设计http://www.maiqicn.com 办公资源网站大全https://www.wode007.com

     下面是业务代码demo

    Demo.tsx  

    import React, { FC } from 'react';
    import { Form, FormComponentProps, Input, Table } from '@slardar/antd';
    import { useFormTable } from '@slardar/common-modules';

    const FormItem = Form.Item;

    interface IProps extends FormComponentProps {}
    const DemoComponent: FC<IProps> = function(props) {
    const form = props.form;
    let [tableState] = useFormTable({
    form: props.form,
    getDataApi: () => Promise.resolve([] as any),
    includeFormFields: ['name', 'search']
    });

    return (
    <div className={'alarm-page-content'}>
    <Form layout={'inline'}>
    <FormItem>
    {form.getFieldDecorator('name')(
    <Input.Search
    style={{ marginLeft: 16, 150 }}
    placeholder="名称"
    />
    )}
    </FormItem>
    <FormItem>
    {form.getFieldDecorator('search')(
    <Input.Search
    style={{ marginLeft: 16, 150 }}
    placeholder="评论"
    />
    )}
    </FormItem>
    </Form>
    <Table {...tableState} columns={[]} />
    </div>
    );
    };

    export const Demo = Form.create()(DemoComponent);

    useDeepCompareEffect.ts  

    import { useRef, useEffect } from 'react';
    import _ from 'lodash';
    export function useDeepCompareEffect<T>(fn, deps: T) {
    // 使用一个数字信号控制是否渲染,简化 react 的计算,也便于调试
    let renderRef = useRef<number | any>(0);
    let depsRef = useRef<T>(deps);
    if (!_.isEqual(deps, depsRef.current)) {
    renderRef.current++;
    }
    depsRef.current = deps;
    return useEffect(fn, [renderRef.current]);
    }
  • 相关阅读:
    CodingTrip
    CodingTrip
    Linux下面查找含有特定的字符的文件
    Linux下TCP/IP协议的Socket编程
    显示Apache服务器里面访问量排在前10的ip地址
    c语言的详细编译过程
    WebStorm设置编辑器中的字体大小
    Sublime多行编辑快捷键
    Aptana 中去掉“Missing semicolon”提醒
    公认的媒体类型
  • 原文地址:https://www.cnblogs.com/xiaonian8/p/13764923.html
Copyright © 2020-2023  润新知