突然发现上一篇文章已经是半年之前了,最近一直在沉迷工作,每天就到掘金看看涨涨姿势。但是领导最近突然跟我说你hooks用的挺多的嘛,要不做个组内技术分享吧。所以又捡起了笔头写了一个类似普及Hooks的文章= =
话说回来,现在我已经不再使用class 组件(除非一些旧项目 或者 对function component支持度不够,如 Taro 编写小程序),而是全面拥抱 hooks ,统一使用函数组件,写代码效率提高了不少。
根据我的实践和对hooks的理解,它带给我最大的感受就是:
- 1.组件逻辑更清晰。摒弃了生命周期函数和class的一些难以理解的特性和坑,组件的渲染就是函数的执行
- 2.提供了更好的状态逻辑复用的途径
- 3.更简洁的代码,粒度更小的组件与逻辑
官方文档:React Hooks中文简介
下面我将会通过实现一个最常见的带筛选项的列表页,来跟大家分享如何使用hooks来编写我们的代码,实现我们的功能。
页面原型
(图片来源:阿里飞冰模板(unpkg.com/@icedesign/…
1.使用useState初始化state
使用data和filters搭配Immutable保存列表和筛选项数据,useState函数参数为state初始值。
import { fromJS } from 'immutable';
import { Table } from 'antd'; // 基础组件基于阿里antd组件库
function TablePage() {
const [data, setData] = useState([]);
const [filters, setFilters] = useState(fromJS({
deliveryTime: [],
orderTime: [],
name: '',
}));
return (
<div>
<Filters /> {/*Filters为具体的筛选组件,这里略过*/}
<Table
dataSource={data}
/>
</div>
);
}
复制代码
2.使用useEffect来执行副作用
useEffect在我看来是最重要的hooks,它给予了我们在组件渲染后实现各种不同的副作用的手段。官方文档介绍useEffect可以看作是componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。所以effect在每一次渲染更新(包括第一次)后都会执行。
这就带来另外一个问题,如何控制useEffect的执行,所以useEffect的第二个参数这时候就发挥了作用,它提供了useEffect是否执行的依据。每次执行useEffect之前,React会判断参数中的每个依赖是否与上次渲染的值是否一致(===判断),若一致就会跳过执行effect,这就达到了控制的效果。
// 当我们想effect仅执行一次的时候,可传入一个空数组,表示该effect不依赖于任何state或者props
useEffect(() => {
effect1();
}, []);
useEffect(() => {
getData(); // 获取数据的函数,依赖于filters的值
}, [filters]);
复制代码
所以我们现在的页面代码看起来就像这样:
function TablePage() {
const [data, setData] = useState([]);
const [filters, setFilters] = useState(fromJS({
deliveryTime: '',
orderTime: '',
name: '',
}));
useEffect(() => {
getData(); // 获取数据的函数,依赖于filters的值
}, [filters]);
function getData() { // 一个通过网络请求获取数据的函数
fetchAPI({
deliveryTime: filters.get('deliveryTime'),
orderTime: filters.get('orderTime'),
name: filters.get('name'),
})
.then((rsp) => {
setData(rsp.data);
});
}
function handleFiltersChange(key, value) {
setFilters(filters.set(key, value);
}
return (
<div>
<Filters onChange={handleFiltersChange} /> {/*Filters为具体的筛选组件,这里略过*/}
<Table
dataSource={data}
/>
</div>
);
}
复制代码
3.自定义Hooks实现逻辑复用
在日常开发中,我们会有很多很多的逻辑其实是重复的,当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。而组件和 Hook 都是函数,所以也同样适用这种方式。这第三个函数就是我们的自定义Hook。
自定义 Hook 是一个函数,其名称以 “use” 开头且必须以 “use” 开头;函数内部可以调用其他的 Hook,且其内部的Hook是完全独立的。
3.1 usePagination实现分页逻辑
function usePagination(paginationOptions) {
const [_pagination, setPagination] = useState(fromJS({
total: 0,
current: 1,
pageSize: 10,
...paginationOptions,
}));
return [_pagination, setPagination];
}
复制代码
3.2 useDebounce实现请求接口防抖
function useDebounce() {
const [timer, setTimer] = useState(0);
function debounce(throttleFunc, ThrottleTime = 250) {
clearTimeout(timer);
setTimer(setTimeout(throttleFunc, ThrottleTime));
}
return debounce;
}
复制代码
这时列表页面中可以像如下这样使用我们的自定义hooks:
function TablePage() {
const [data, setData] = useState([]);
const [filters, setFilters] = useState(fromJS({
deliveryTime: '',
orderTime: '',
name: '',
}));
const [pagination, setPaination] = usePagination();
const debounce = useDebounce(); // 获取debounce函数
useEffect(() => {
debounce(getData); // 获取数据的函数,依赖于filters的值
}, [filters, pagination]);
function getData() { // 一个通过网络请求获取数据的函数
fetchAPI({
deliveryTime: filters.get('deliveryTime'),
orderTime: filters.get('orderTime'),
name: filters.get('name'),
})
.then((rsp) => {
setData(rsp.data);
});
}
function handleFiltersChange(key, value) {
setFilters(filters.set(key, value);
}
function handleTableChange(_pagination) {
setPaination(
pagination.set('current', _pagination.current)
.set('pageSize', _pagination.pageSize)
);
}
return (
<div>
<Filters onChange={handleFiltersChange} /> {/*Filters为具体的筛选组件,这里略过*/}
<Table
columns={/* wirte your own columns config */}
dataSource={data}
onChange={handleTableChange}
/>
</div>
);
复制代码
想了解更多自定义hooks的玩法,可以了解一下以下文章:
2019年了,整理了N个实用案例帮你快速迁移到React Hooks
React hooks 的尝试使用 (我的关于hooks的上一篇文章,包括useContext合useReducer的一些使用和一些自定义hooks)
4.总结
这样我们基本上就实现了一个用Hooks管理的函数组件了,可能这一个简单的案列还不能完全体现hooks的优势,还有一些优化手段诸如useMemo和useCallback搭配HOC,还有最近在使用的TypeScript,这里只是抛砖引玉。 最后欢迎大家给我提意见,多交流,让我们在掘金共同进步!