美文网首页Reactreact
利用react hook封装业务(搜索框和列表)

利用react hook封装业务(搜索框和列表)

作者: 你的时间非常值钱 | 来源:发表于2019-10-12 14:00 被阅读0次

    阅读本文需一点react hook基础(react的16.7beta版、16.8正式版,文中涉及useState,useEffect最基本用法)

    项目.png

    分析到这个系统具有多个这种相同结构的页面,就是组合搜索框(Searchgroup)和列表(Table)两大组合式组件,都是对antd的二次封装,在此不介绍如何封装,先看原本业务代码

    
    // xx/index.js 容器页面
    ...
    ...
    const FETCH_API = 'xxx/xxx';
    
    class Index extends Component {
      // 存搜索框的值
      state = {
        values: {},
        selectRowKeys: [], // 列表选择的key数组
      }
      // 分页请求
      fetchList = (page = 1, pageSize = 10) => {
        const { values } = this.state
        this.props.dispatch({
          type: FETCH_API ,
          payload: {
            page,
            pageSize,
            ...values,
          }
        })
      }
     // 查询触发
      handleSearch = values => {
        this.setState({values}, this.fetchList)
      }
      render() {
        const { list, page, loading } = this.props
        const { selectRowKeys } = this.state
        const rowSelection = {
          selectRowKeys,
          onChange: selectRowKeys  => this.setState({selectRowKeys })
        }
        return (
          <>
            <Card>
              <Searchgroup
                config={config} 
                handleSearch={this.handleSearch}
              />
            </Card>
            <Card>
              <Table
                columns={columns}
                dataSource={list}
                pagination={page}
                loading={loading }
                rowSelection={rowSelection}
                rowKey='xxxId'
              />
            </Card>
          </>
        )
      }
    }
    export default connect(({ test, loading }) => ({
      list: test.list,
      page: test.page,
      loading: loading.effects[FETCH_API],
    }))(Index);
    
    分析了好几个页面(基本全部容器组件),都存在几个重复的逻辑
    • 查询动作,handleSearch,用来更新state中的values,回调作为fetchList参数
    • 根据state的values拉取接口返回分页(fetchList),effect后触发到各个modal的分发,引起Table组件loading,dataSource,pagination等属性变化
    • 列表的所选id还有触发动作(rowSelection)
      ...

    我会想能不能写少点代码,只要传入相应配置就可以达到逻辑复用,这里,我选择用函数组件(+hook)代替类组件,因为
    1.类组件比较倾向于面向生命周期编写,业务逻辑中相同一个方法很大可能会重复出现在不同生命周期,例如componentDidMount,componentDidUpdate,componentWillUnmount很容易会出现相同的方法,而用hook倾向于面向业务编写,一般情况一个useEffect搞定
    2.class的热重载不稳定(有待考究)
    ...
    PS: react16.7+的函数式组件才开始有处理内部state能力,之前的版本都是纯props组件,还有hook只能写在函数组件里或者自定义hook里

    下面是自定义hook代码

    // hooks.js
    import { useState, useEffect } from 'react';
    
    export const usePageList = ({ list, page, loading }, api) => {
      // 注意useState的值顺序不能变,因为hook内部的值是用链表存储的,例如最好不要在外围加条件判断
      const [values, setValues] = useState({});
      const [selectRowKeys, setSelectRowkeys] = useState([]);
    
      const handleSearch = values => {
        setValues(values);
      };
    
      const fetch = (page = 1, pageSize = 10) => {
       // 为了封装性,直接用全局的dispatch,不用手动传进来了
        window.g_app._store.dispatch({
          type: api,
          payload: {
            page,
            pageSize,
            ...values
          }
        });
      };
      // 根据values变化触发拉取分页和重置选择的动作
      useEffect(() => {
        fetch()
        setSelectRowkeys([])
      }, [values]);
      
     // 搜索框的属性
      const searchProps = {
        handleSearch,
      };
     
    // 分页表格的属性
      const tableProps = {
        dataSource: list,
        pagination: page,
        onChange: ({ current, pageSize }) => fetch(current, pageSize),
        loading,
        rowSelection: {
          selectRowKeys,
          onChange: k => setSelectRowkeys(k),
        },
      };
    
      return {
        values,
        searchProps,
        tableProps,
        selectRowKeys,
      };
    };
    
    

    用法

    // xx/index.js 容器页面
    ...
    import { usePageList } from 'hooks'; 
    ...
    const FETCH_API =  'xxx/xxx';
    
    // props其实可以干净点,只写{list,page,loading}就够用,但为了以后拓展和少些点代码(还有懒)的原 因,直接一个大props完事
    const Index = props => {
      // 由自定义hook返回几个属性,直接提供给相应子组件
      const { values, searchProps, tableProps, selectRowKeys } = usePageList(props, FETCH_API);
      return (
        <>
          <Card>
            <Searchgroup
              config={config}
              {...searchProps}
            />
          </Card>
          <Card>
            <Table
              columns={columns}
              rowKey="xxxId"
              {...tableProps}
            />
          </Card>
        </>
      );
    };
    
    export default connect(({ test, loading }) => ({
      list: test.list,
      page: test.page,
      loading: loading.effects[FETCH_API],
    }))(Index);
    

    完事,业务逻辑直接封装好,只需抽离出个性部分就就可以了,省去大量代码,具有了封装性还留着可拓展的可能性。其实里面还可以用多个自定义hook组合,例如原本还想把selectRowKeys逻辑部分抽出单独hook处理,但为了不过度抽象就先放下了,毕竟适合业务和有高的开发体验才是最重要的

    相关文章

      网友评论

        本文标题:利用react hook封装业务(搜索框和列表)

        本文链接:https://www.haomeiwen.com/subject/qsqqmctx.html