In this article, I want to introduce you to the practice of React Hooks on the list page, mainly through useReducer and useEffect. And to express my understanding and thinking about React Hooks.

If you don’t know React Hooks, click on the introduction to Hooks; If you want to see the final implementation directly, please click warehouse.

Usage scenarios

In daily development, we often encounter some list page development requirements.

The most basic function of a list page is the display of the list, and the display of the list requires a lot of state management, such as: list data, total data, current page, display number, and so on. These states are, for the most part, common to all list pages, i.e. have common logic.

None of the previous solutions was particularly good enough to share this logic code. High-level component HOC works, but introduces the problem of too deep a component hierarchy. (If you are interested, you can find out what higher-order components are for. This article does not go into that.)

The good news is that React Hooks can help us do that.

What do you do

If we want to implement a custom Hook useTable, briefly explain the function

  1. To receive aurl, exposed to the outside{ onSearch, bind: { loading, dataSource, pagination: { current, pageSize, total }, onChange } }. This is based onantdDeveloped, sobindThe inside thing is bound inantdTableOn the component.
  2. Data is automatically requested when the page is initialized
  3. Cancel subsequent operations of asynchronous requests when the page unloads
  4. onChangeonSearchAutomatically request data when
  5. In loading, no new asynchronous requests are triggered

Okay, without further ado, let’s get right to the code.

// 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: {},}// Serve as reducer in the useReducer
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:
      returnstate; }}export default (url, initState = {}) => {
  /** * useReducer returns a dispatch function that is passed an action and has a reducer function for data processing */
  const [{
    loading, / / load state
    current, / / the current page
    pageSize, // How many pages per page
    total, // How many items in total
    order, // Sort direction
    field, // Sort the fields
    dataSource, / / data
    params, // Additional search terms}, dispatch] = useReducer(reducer, { ... DEFAULT_STATE, ... initState, });// Hooks to get data
  useEffect((a)= > {
    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((a)= > dispatch({ type: STATE_CHANGE, data: { loading: false}}));// Returns a value that is called after the page is unloaded
    return (a)= > cancel = true;
  }, [url, current, pageSize, order, field, params]); // The function is automatically called when these states change

  // Search for events
  function onSearch(nextParams) {
    // Click the search button to jump to the first page! loading && dispatch({type: STATE_CHANGE, data: { params: nextParams, current: 1}}); }// Change events
  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 () {
  // Use custom hooks
  const { onSearch, bind } = useTable(url);
  const columns = [
    { title: 'number'.dataIndex: 'id' },
    { title: 'name'.dataIndex: 'name' },
    { title: 'age'.dataIndex: 'age' },
    { title: 'email'.dataIndex: 'email' },
    { title: 'home'.dataIndex: 'url' },
    { title: 'city'.dataIndex: 'city'},];return( <Fragment> <SearchForm onSearch={onSearch}/> <Table rowKey={'id'} columns={columns} {... bind} /> </Fragment> ); } export default UseHooksTable;Copy the code

The comments in the code explain the code briefly and should not be too difficult.

What problem was solved

For now, let’s think about what React Hooks can do for us. To do this, I wrote an additional list page using the class method, as shown below

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: 'number'.dataIndex: 'id'.sorter: true },
    { title: 'name'.dataIndex: 'name'.sorter: true },
    { title: 'age'.dataIndex: 'age'.sorter: true },
    { title: 'email'.dataIndex: 'email'.sorter: true },
    { title: 'home'.dataIndex: 'url'.sorter: true },
    { title: 'city'.dataIndex: 'city'.sorter: true},]; componentDidMount() {this.getData();
  }
  componentWillUnmount() {
    this.cancel = true;
  }
  // Search for events
  handleSearch = (nextParams) = > {
    // Click the search button to jump to the first page
    !this.state.loading && this.setState({ params: nextParams, current: 1 }, this.getData);
  }
  // Change events
  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((a)= > this.setState({ loading: false }));
    });
  }
  render() {
    const {
      loading, / / load state
      current, / / the current page
      pageSize, // How many pages per page
      total, // How many items in total
      dataSource, / / data
    } = 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;
Copy the code

Use Hooks on all pages that have common logic. The number of lines of code can be reduced from 80 to 30. The code is easy to understand, easy to use, and reusability is improved.

That should be the charm of Hooks.

conclusion

We can imagine future communities providing us with interesting Hooks.

In our own development, we could also use Hooks to encapsulate our common logic, which would be much more efficient.

And most importantly, Hooks make our code beautiful.

Well, this is important, hahahahahahahahaha.