基用React Hooks + Antd快速实现一个列表页

突然发现上一篇文章已经是半年之前了,最近一直在沉迷工作,每天就到掘金看看涨涨姿势。但是领导最近突然跟我说你hooks用的挺多的嘛,要不做个组内技术分享吧。所以又捡起了笔头写了一个类似普及Hooks的文章= =

话说回来,现在我已经不再使用class 组件(除非一些旧项目 或者 对function component支持度不够,如 Taro 编写小程序),而是全面拥抱 hooks ,统一使用函数组件,写代码效率提高了不少。

根据我的实践和对hooks的理解,它带给我最大的感受就是:

  • 1.组件逻辑更清晰。摒弃了生命周期函数和class的一些难以理解的特性和坑,组件的渲染就是函数的执行
  • 2.提供了更好的状态逻辑复用的途径
  • 3.更简洁的代码,粒度更小的组件与逻辑

官方文档:React Hooks中文简介

下面我将会通过实现一个最常见的带筛选项的列表页,来跟大家分享如何使用hooks来编写我们的代码,实现我们的功能。

页面原型 页面原型 (图片来源:阿里飞冰模板(unpkg.com/@icedesign/…

1.使用useState初始化state


使用data和filters搭配Immutable保存列表和筛选项数据,useState函数参数为state初始值。

import { fromJS } from 'immutable';
import { Table } from 'antd'; // 基础组件基于阿里antd组件库

function TablePage() {
    const [data, setData] = useState([]);
    const [filters, setFilters] = useState(fromJS({
        deliveryTime: [],
        orderTime: [],
        name: '',
    }));
    
    return (
        <div>
            <Filters /> {/*Filters为具体的筛选组件,这里略过*/}
            <Table
                dataSource={data}
            />
        </div>
    );
}

复制代码

2.使用useEffect来执行副作用


useEffect在我看来是最重要的hooks,它给予了我们在组件渲染后实现各种不同的副作用的手段。官方文档介绍useEffect可以看作是componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。所以effect在每一次渲染更新(包括第一次)后都会执行。

这就带来另外一个问题,如何控制useEffect的执行,所以useEffect的第二个参数这时候就发挥了作用,它提供了useEffect是否执行的依据。每次执行useEffect之前,React会判断参数中的每个依赖是否与上次渲染的值是否一致(===判断),若一致就会跳过执行effect,这就达到了控制的效果。

// 当我们想effect仅执行一次的时候,可传入一个空数组,表示该effect不依赖于任何state或者props
useEffect(() => {
    effect1();
}, []);

useEffect(() => {
    getData(); // 获取数据的函数,依赖于filters的值
}, [filters]);

复制代码

所以我们现在的页面代码看起来就像这样:

function TablePage() {
    const [data, setData] = useState([]);
    const [filters, setFilters] = useState(fromJS({
        deliveryTime: '',
        orderTime: '',
        name: '',
    }));
    
    useEffect(() => {
        getData(); // 获取数据的函数,依赖于filters的值
    }, [filters]);
    
    function getData() { // 一个通过网络请求获取数据的函数
        fetchAPI({
            deliveryTime: filters.get('deliveryTime'),
            orderTime: filters.get('orderTime'),
            name: filters.get('name'),
        })
            .then((rsp) => {
                setData(rsp.data);
            });
    }
    
    function handleFiltersChange(key, value) {
        setFilters(filters.set(key, value);
    }
    
    return (
        <div>
            <Filters onChange={handleFiltersChange} /> {/*Filters为具体的筛选组件,这里略过*/}
            <Table
                dataSource={data}
            />
        </div>
    );
}

复制代码

3.自定义Hooks实现逻辑复用


在日常开发中,我们会有很多很多的逻辑其实是重复的,当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。而组件和 Hook 都是函数,所以也同样适用这种方式。这第三个函数就是我们的自定义Hook。
自定义 Hook 是一个函数,其名称以 “use” 开头且必须以 “use” 开头;函数内部可以调用其他的 Hook,且其内部的Hook是完全独立的。

3.1 usePagination实现分页逻辑

function usePagination(paginationOptions) {
  const [_pagination, setPagination] = useState(fromJS({
    total: 0,
    current: 1,
    pageSize: 10,
    ...paginationOptions,
  }));

  return [_pagination, setPagination];
}
复制代码

3.2 useDebounce实现请求接口防抖

function useDebounce() {
  const [timer, setTimer] = useState(0);

  function debounce(throttleFunc, ThrottleTime = 250) {
    clearTimeout(timer);
    setTimer(setTimeout(throttleFunc, ThrottleTime));
  }

  return debounce;
}
复制代码

这时列表页面中可以像如下这样使用我们的自定义hooks:

function TablePage() {
    const [data, setData] = useState([]);
    const [filters, setFilters] = useState(fromJS({
        deliveryTime: '',
        orderTime: '',
        name: '',
    }));
    const [pagination, setPaination] = usePagination();
    const debounce = useDebounce(); // 获取debounce函数
    
    useEffect(() => {
        debounce(getData); // 获取数据的函数,依赖于filters的值
    }, [filters, pagination]);
    
    function getData() { // 一个通过网络请求获取数据的函数
        fetchAPI({
            deliveryTime: filters.get('deliveryTime'),
            orderTime: filters.get('orderTime'),
            name: filters.get('name'),
        })
            .then((rsp) => {
                setData(rsp.data);
            });
    }
    
    function handleFiltersChange(key, value) {
        setFilters(filters.set(key, value);
    }
    
    function handleTableChange(_pagination) {
        setPaination(
            pagination.set('current', _pagination.current)
                .set('pageSize', _pagination.pageSize)
        );
    }
    
    return (
        <div>
            <Filters onChange={handleFiltersChange} /> {/*Filters为具体的筛选组件,这里略过*/}
            <Table
                columns={/* wirte your own columns config */}
                dataSource={data}
                onChange={handleTableChange}
            />
        </div>
    );
复制代码

想了解更多自定义hooks的玩法,可以了解一下以下文章:
2019年了,整理了N个实用案例帮你快速迁移到React Hooks
React hooks 的尝试使用 (我的关于hooks的上一篇文章,包括useContext合useReducer的一些使用和一些自定义hooks)

4.总结


这样我们基本上就实现了一个用Hooks管理的函数组件了,可能这一个简单的案列还不能完全体现hooks的优势,还有一些优化手段诸如useMemo和useCallback搭配HOC,还有最近在使用的TypeScript,这里只是抛砖引玉。 最后欢迎大家给我提意见,多交流,让我们在掘金共同进步!

分类:
前端
标签: