内容简介:在本文中,我想向大家介绍如果你还不知道在日常的开发中,我们常常会碰到一些列表页的开发需求。
在本文中,我想向大家介绍 React Hooks
在列表页中的实践,主要是通过 useReducer
和 useEffect
来实现的。以及表达一下我对 React Hooks
的理解与思考。
如果你还不知道 React
的这个新特性 React Hooks
,那么点击Hooks 简介 ; 如果你想看直接查看最后的实现效果,请点击 仓库
。
使用场景
在日常的开发中,我们常常会碰到一些列表页的开发需求。
一个列表页的最基本的功能就是列表的展示,而列表的展示就需要很多 state
去管理,比如:列表数据、数据总数、当前页、展示条数等等。而这些 state
绝大部分是所有列表页的都通用的即有共同的逻辑。
在以往的方案中还没有特别好的方案,可以共用这些逻辑代码。高阶组件 HOC
可以,但会引入组件层级过深的问题。(如读者有兴趣,可自行去了解高阶组件的用途,本文不深入讨论)。
好消息是, React Hooks
就能帮助我们完成这个心愿。
做什么
我们要是现实一个自定义 Hook useTable
,简单说明一下功能
-
接收一个
url,向外暴露{ onSearch, bind: { loading, dataSource, pagination: { current, pageSize, total }, onChange } }。 这个是基于antd开发的,所以bind内的东西是绑定在antd的Table组件上的。 - 在页面初始化时,自动请求数据
- 在页面卸载时,取消异步请求的后续操作
-
onChange和onSearch时自动请求数据 - 在 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
可以让我们的 代码变得美观
。
嗯,这很重要,哈哈哈哈哈哈。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Effective JavaScript
David Herman / Addison-Wesley Professional / 2012-12-6 / USD 39.99
"It's uncommon to have a programming language wonk who can speak in such comfortable and friendly language as David does. His walk through the syntax and semantics of JavaScript is both charming and h......一起来看看 《Effective JavaScript》 这本书的介绍吧!