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
- To receive a
url
, exposed to the outside{ onSearch, bind: { loading, dataSource, pagination: { current, pageSize, total }, onChange } }
. This is based onantd
Developed, sobind
The inside thing is bound inantd
的Table
On the component.- Data is automatically requested when the page is initialized
- Cancel subsequent operations of asynchronous requests when the page unloads
onChange
和onSearch
Automatically request data when- 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.