Hook改变的React Component写法思路(4) – useReducer

释放双眼,带上耳机,听听看~!

其实一开始看到useReducer的时候,还以为它跟react的好伙伴redux有什么关系。然而也就是借鉴了redux的设计模式的状态操作工具而已。

目前使用Redux的小伙伴们可能还需要用原来的react-redux提供的connect HOC一段时间。尽管facebook已经在做相关的hook,目前还处于不稳定的状态,使用后果自负(项目地址:https://github.com/facebookincubator/

回到useReducer,它的存在是为一步操作更新多个状态设计。

举个不太恰当的拉数据的例子。通常拉数据的时候需要显示表示正在“加载中”的动画,加载数据出错则需要提示相关的错误,成功则显示数据。这里我们假设有2个状态值isLoading和 error,加载成功的数据则是由react-redux从props里传进来:

  • 加载中:isLoading = true; error = undefined; data = undefined | { … };
  • 成功:isLoading = false; error = undefined; data = { … };
  • 失败:isLoading = false; error = ‘Error message.’; data = { … };

比较容易想到的做法是用useState,如下:


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
1function MyComponent({ loadDataAction, data }) {
2   const [isLoading, setIsLoading] = useState(false);
3   const [error, setError] = useState(undefined);
4   const loadData = useCallback(async () => {
5        setIsLoading(true);
6        setError(undefined);
7        try {
8            await loadDataAction();
9        } catch(err) {
10            setError(err);
11        } finally {
12            setIsLoading(false);
13        }
14   });
15   return (
16        <div>
17            <button onClick={loadData} disabled={isLoading}>Load Data</button>
18            {error ? (
19                <p>{error}</p>
20                ) :  
21                data ? (
22                    <h3>Loaded data below</h3>
23                    <div>{data}</div>
24                ) : null
25        </div>
26   );
27}
28
29

改成useReducer的话每种操作对状态造成什么影响会更加清晰,并且易于重用:


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
1function loadStateReducer(state, action) {
2    switch (action.type) {
3        case ‘loading’:
4            return {
5                isLoading: true,
6                error: undefined
7            };
8        case ‘success’;
9            return {
10                isLoading: false,
11                error: undefined
12            };
13        case ‘error’:
14            return {
15                isLoading: false,
16                error: action.error
17            };
18    }
19    return state;
20}
21
22function MyComponent({ loadDataAction, data }) {
23   const [state, dispatch] = useReducer(loadStateReducer, {
24        isLoading: false,
25        error: undefined
26   });
27   const { isLoading, error } = state;
28   const loadData = useCallback(async () => {
29        dispatch({ type: ‘loading’ });
30        try {
31            await loadDataAction();
32            dispatch({ type: ‘success’ });
33        } catch(err) {
34            dispatch({ type: ‘error’, error: err });
35        }
36   });
37   return (
38        <div>
39            <button onClick={loadData} disabled={isLoading}>Load Data</button>
40            {error ? (
41                <p>{error}</p>
42                ) :  
43                data ? (
44                    <h3>Loaded data below</h3>
45                    <div>{data}</div>
46                ) : null
47        </div>
48   );
49}
50
51

讲真,用useState设置一个object state是等同的操作。所以用哪个看个人喜好吧。

这里要注意的是,当两个state值有相关性(比如B是根据A计算得到的结果),那就有必要考虑用object类型的state或者useReducer,否则容易遇到上下文不一致导致出现非自己期望的结果(遇到过一次,容我想到恰当的例子再谈这个问题)。

另外,我说这个例子不恰当是因为这个例子中没有在使用异步操作后考虑component实例是否还是挂载的状态,可能导致内存泄漏,并且也会有相关的报错。

给TA打赏
共{{data.count}}人
人已打赏
安全技术

md5 加密算法

2021-8-18 16:36:11

安全技术

C++ 高性能服务器网络框架设计细节

2022-1-11 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索