自定义hooks
声明一个名字以 use 开头的函数, 并在函数中用到其他hooks。
Hooks 和普通函数在语义上的区别,就在于函数中有没有用到其它 Hooks(自定义或者内置,能够让组件刷新,或者去产生副作用),没有用到就是普通函数。
典型的四个使用场景
- 抽取业务逻辑;
- 封装通用逻辑;
- 监听浏览器状态;
- 拆分复杂组件.
抽取业务逻辑
抽取具体业务逻辑到hooks中,暴露接口在组件中调用
一方面能让这个逻辑得到重用,另外一方面也能让代码更加语义化,并且易于理解和维护1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34// 实现计数器业务逻辑的拆分和重用
import { useState, useCallback }from 'react';
function useCounter() {
// 定义 count 这个 state 用于保存当前数值
const [count, setCount] = useState(0);
// 实现加 1 的操作
const increment = useCallback(() => setCount(count + 1), [count]);
// 实现减 1 的操作
const decrement = useCallback(() => setCount(count - 1), [count]);
// 重置计数器
const reset = useCallback(() => setCount(0), []);
// 将业务逻辑的操作 export 出去供调用者使用
return { count, increment, decrement, reset };
}
// 应用
import React from 'react';
function Counter() {
// 调用自定义 Hook
const { count, increment, decrement, reset } = useCounter();
// 渲染 UI
return (
<div>
<button onClick={decrement}> - </button>
<p>{count}</p>
<button onClick={increment}> + </button>
<button onClick={reset}> reset </button>
</div>
);
}
封装通用逻辑:useAsync
在每个需要异步请求的组件中,其实都需要重复相同的逻辑。
事实上,在处理这类请求的时候,模式都是类似的,通常都会遵循下面步骤:
- 创建 data,loading,error 这 3 个 state;
- 请求发出后,设置 loading state 为 true;
- 请求成功后,将返回的数据放到某个 state 中,并将 loading state 设为 false;
- 请求失败后,设置 error state 为 true,并将 loading state 设为 false。
最后,基于 data、loading、error 这 3 个 state 的数据,
UI 就可以正确地显示数据,或者 loading、error 这些反馈给用户了。
所以,通过创建一个自定义 Hook,可以很好地将这样的逻辑提取出来,成为一个可重用的模块。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import { useState } from 'react';
// asyncFunction 真正发出请求的函数
const useAsync = (asyncFunction) => {
// 设置三个异步逻辑相关的 state
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// 定义一个 callback 用于执行异步逻辑 相当于声明一个请求的函数,在需要请求数据的情况下调用
const execute = useCallback(() => {
// 请求开始时,设置 loading 为 true,清除已有数据和 error 状态
setLoading(true);
setData(null);
setError(null);
return asyncFunction()
.then((response) => {
// 请求成功时,将数据写进 state,设置 loading 为 false
setData(response);
setLoading(false);
})
.catch((error) => {
// 请求失败时,设置 loading 为 false,并设置错误状态
setError(error);
setLoading(false);
});
}, [asyncFunction]);
return { execute, loading, data, error };
};
// 应用
import React from "react";
import useAsync from './useAsync';
export default function UserList() {
// 通过 useAsync 这个函数,只需要提供异步逻辑的实现
const {
execute: fetchUsers, // fetchUsers可以在需要请求的地方去使用
data: users, // 返回的数据可以直接拿来渲染ui
loading,
error,
} = useAsync(async () => {
const res = await fetch("https://reqres.in/api/users/");
const json = await res.json();
return json.data;
});
return (
// 根据状态渲染 UI...
);
}
利用了 Hooks 能够管理 React 组件状态的能力,将一个组件中的某一部分状态独立出来,从而实现了通用逻辑的重用。
监听浏览器状态:useScroll
1 |
|
拆分复杂组件
拆分逻辑的目的不一定是为了重用,而可以是仅仅为了业务逻辑的隔离。
把 Hooks 就看成普通的函数,能隔离的尽量去做隔离
1 |
|