React Hooks 在列表页中的实践

栏目: 服务器 · 发布时间: 6年前

内容简介:在本文中,我想向大家介绍如果你还不知道在日常的开发中,我们常常会碰到一些列表页的开发需求。

在本文中,我想向大家介绍 React Hooks 在列表页中的实践,主要是通过 useReduceruseEffect 来实现的。以及表达一下我对 React Hooks 的理解与思考。

如果你还不知道 React 的这个新特性 React Hooks ,那么点击Hooks 简介 ; 如果你想看直接查看最后的实现效果,请点击 仓库

使用场景

在日常的开发中,我们常常会碰到一些列表页的开发需求。

一个列表页的最基本的功能就是列表的展示,而列表的展示就需要很多 state 去管理,比如:列表数据、数据总数、当前页、展示条数等等。而这些 state 绝大部分是所有列表页的都通用的即有共同的逻辑。

在以往的方案中还没有特别好的方案,可以共用这些逻辑代码。高阶组件 HOC 可以,但会引入组件层级过深的问题。(如读者有兴趣,可自行去了解高阶组件的用途,本文不深入讨论)。

好消息是, React Hooks 就能帮助我们完成这个心愿。

做什么

我们要是现实一个自定义 Hook useTable ,简单说明一下功能

  1. 接收一个 url ,向外暴露 { onSearch, bind: { loading, dataSource, pagination: { current, pageSize, total }, onChange } } 。 这个是基于 antd 开发的,所以 bind 内的东西是绑定在 antdTable 组件上的。
  2. 在页面初始化时,自动请求数据
  3. 在页面卸载时,取消异步请求的后续操作
  4. onChangeonSearch 时自动请求数据
  5. 在 loading 中,不会触发新的异步请求

好了话不多说,直接上代码。

// useTable.js
import { useReducer, useEffect } from 'react';
import axios from 'axios';

// action type
const DATA_CHANGE = 'DATA_CHANGE';
const STATE_CHANGE = 'STATE_CHANGE';

const DEFAULT_STATE = {
  loading: false,
  current: 1,
  pageSize: 10,
  total: 0,
  order: false,
  field: '',
  dataSource: [],
  params: {},
}

// 用作 useReducer 中的 reducer
const reducer = (state, action) => {
  const { type, data: { dataSource, ...nextState } } = action;
  switch (type) {
    case STATE_CHANGE:
      return {...state, ...nextState};
    case DATA_CHANGE:
      return {...state, dataSource, ...nextState};
    default:
      return state;
  }
}

export default (url, initState = {}) => {
  /**
   * useReducer 的概念和 redux 很像
   * 会返回一个 dispatch 函数,调用的时候传给它一个 action 
   * 相应的会有一个 reducer 函数,用于数据处理
   */
  const [{
    loading, // 加载态
    current, // 当前页
    pageSize, // 一页多少条
    total, // 总共多少条
    order, // 排序方向
    field, // 排序字段
    dataSource, // 数据
    params, // 额外搜索项
  }, dispatch] = useReducer(reducer, {
    ...DEFAULT_STATE,
    ...initState,
  });
    
  // 获取数据的 hooks
  useEffect(() => {
    let cancel = false;
    dispatch({ type: STATE_CHANGE, data: { loading: true } });

    axios.post(
      url,
      { current, pageSize, order, field, ...params },
    ).then(({ data, status }) => {
      if (status === 200) return data;
    }).then(({ data = [], total }) => {
      !cancel && dispatch({ type: DATA_CHANGE, data: { dataSource: data, total }});
    }).finally(() => dispatch({ type: STATE_CHANGE, data: { loading: false } }));
    
    // 返回值时页面卸载之后调用的函数
    return () => cancel = true;
  }, [url, current, pageSize, order, field, params]); // 当这几个状态改变时自动调用函数

  // 搜索事件
  function onSearch(nextParams) {
    // 点击搜索按钮 跳到第一页
    !loading && dispatch({ type: STATE_CHANGE, data: { params: nextParams, current: 1 } });
  }

  // 变更事件
  function onChange({ current, pageSize }, filters, { order = false, field = ''}) {
    !loading && dispatch({ type: STATE_CHANGE, data: { current, pageSize, order, field }});
  }

  return {
    onSearch,
    bind: {
      loading,
      dataSource,
      pagination: { current, pageSize, total },
      onChange,
    }
  };
}

// UseHooksTable.js
import React, { Fragment } from 'react';
import { Table } from 'antd';
import SearchForm from './SearchForm';
import useTable from './useTable';

const url = 'https://www.easy-mock.com/mock/5cf8ead34758621a19eef994/getData';
function UseHooksTable () {
  // 使用自定义 hook
  const { onSearch, bind } = useTable(url);
  const columns = [
    { title: '编号', dataIndex: 'id' },
    { title: '姓名', dataIndex: 'name' },
    { title: '年龄', dataIndex: 'age' },
    { title: '邮箱', dataIndex: 'email' },
    { title: '主页', dataIndex: 'url' },
    { title: '城市', dataIndex: 'city' },
  ];
  return (
    <Fragment>
      <SearchForm onSearch={onSearch}/>
      <Table
        rowKey={'id'}
        columns={columns}
        {...bind}
      />
    </Fragment>
  );
}

export default UseHooksTable;

复制代码

在代码中的注释简单解释了一下代码,应该也没有什么难点。

解决了什么问题

到此为止,我们应该思考 React Hooks 可以给我们带来些什么。 为此,我额外的写了一个使用 class 方式实现的列表页,下面上代码

import React, { Component, Fragment } from 'react';
import { Table } from 'antd';
import axios from 'axios';
import SearchForm from './SearchForm';

const url = 'https://www.easy-mock.com/mock/5cf8ead34758621a19eef994/getData';
class UseClassTable extends Component {
  state = {
    loading: false,
    current: 1,
    pageSize: 10,
    total: 0,
    order: 0,
    field: '',
    params: {
      name: '',
    },
    dataSource: [],
  }
  cancel = false;
  columns = [
    { title: '编号', dataIndex: 'id', sorter: true },
    { title: '姓名', dataIndex: 'name', sorter: true },
    { title: '年龄', dataIndex: 'age', sorter: true },
    { title: '邮箱', dataIndex: 'email', sorter: true },
    { title: '主页', dataIndex: 'url', sorter: true },
    { title: '城市', dataIndex: 'city', sorter: true },
  ];
  componentDidMount() {
    this.getData();
  }
  componentWillUnmount() {
    this.cancel = true;
  }
  // 搜索事件
  handleSearch = (nextParams) => {
    // 点击搜索按钮 跳到第一页
    !this.state.loading && this.setState({ params: nextParams, current: 1 }, this.getData);
  }
  // 变更事件
  handleTableChange = ({ current, pageSize }, filters, { order = false, field = ''}) => {
    !this.state.loading && this.setState({ current, pageSize, order, field }, this.getData);
  }
  getData() {
    const { current, pageSize, order, field, params } = this.state;
    this.setState({ loading: true }, () => {
      axios.post(
        url,
        { current, pageSize, order, field, ...params },
      ).then(({ data, status }) => {
        if (status === 200) return data;
      }).then(({ data = [], total }) => {
        !this.cancel && this.setState({ dataSource: data, total });
      }).finally(() => this.setState({ loading: false }));
    });
  }
  render() {
    const {
      loading, // 加载态
      current, // 当前页
      pageSize, // 一页多少条
      total, // 总共多少条
      dataSource, // 数据
    } = this.state;
    return (
      <Fragment>
        <SearchForm onSearch={this.handleSearch}/>
        <Table
          rowKey={'id'}
          loading={loading}
          columns={this.columns}
          pagination={{ current, pageSize, total }}
          dataSource={dataSource}
          onChange={this.handleTableChange}
        />
      </Fragment>
    )
  }
}

export default UseClassTable;
复制代码

我们可以看到使用 Hooks 的方式,我们可以把共有的逻辑封装到 Hooks 中,在所有有共有逻辑的页面都使用这样的 hook ,代码行数可以从原先的80行减少到30行,代码变得简单易懂,使用起来也很简单,提高的代码的复用性。

这应该就是 Hooks 的魅力。

结语

我们可以想象到,以后的社区会提供给我们有趣的 Hooks

在我们自己的开发中,也可以使用 Hooks 来封装我们的共有逻辑,效率可以大大提高。

而且最重要的是, Hooks 可以让我们的 代码变得美观

嗯,这很重要,哈哈哈哈哈哈。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Computers and Intractability

Computers and Intractability

M R Garey、D S Johnson / W. H. Freeman / 1979-4-26 / GBP 53.99

This book's introduction features a humorous story of a man with a line of people behind him, who explains to his boss, "I can't find an efficient algorithm, but neither can all these famous people." ......一起来看看 《Computers and Intractability》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具