8
原文:React Hooks使用实例(二)| AlloyTeam
作者:TAT.zhongzhong

上篇文章我们讲了如何使用React的Suspense组件和lazy方法来实现模块的懒加载,后面还讲了如何使用
React的useState方法来实现自定义的Hooks,从而达到复用的目的。

我们知道,不管在做什么样的前端项目,列表页肯定是存在的,那如何获取列表的数据呢?大部分情况下我们都是在每个模块内部自己实现一个获取数据的方法,然后调用setState来更新数据。那有没有更好的方式可以做到这些,并且能够在一个项目中处处复用这个功能呢?答案就是使用React Hooks。

useEffect介绍

简单的说,useEffect就是在组件挂载完成或者更新完成的时候,需要执行的一系列操作,这些操作可能是ajax请求,dom操作,事件处理等等。

官方文档里面有句话说的是,useEffect是 componentDidMount, componentDidUpdate, 和 componentWillUnmount三个生命周期钩子的组合,那就是之前分别在这三个地方干的事情,现在可以统一在一个地方干了,是不是很方便?。为了保证文章简洁,这里不过多介绍,有需要可以参考官方文档

useReducer介绍

如果你用过redux,那你应该知道redux就是通过reducer来处理dispatch出来的各种action的。每个reducer都是一个纯函数,处理完成之后,返回新的state,然后触发React的更新。官方文档

自定义一个获取数据的React Hooks

先来分析下,我们要从服务器获取数据,需要做哪些事情。

  1. 构造请求参数
  2. 发送请求
  3. 解析返回结果或异常处理
  4. 展示结果或异常错误提示

从上面的几个点我们可以分析出,我们的自定义Hook要能够传入请求人url以及请求的参数,在请求失败的时候能够有后台返回的提示信息,在请求成功的时候能够返回后台返回的数据,我们还需要知道请求是否失败。

这里我们将action拆分为3个:

  • FETCH_INIT // 开始加载数据,用来展示Loading状态
  • FETCH_SUCCESS // 加载数据成功,用来展示数据
  • FETCH_ERROR // 加载数据失败,用来展示错误信息

基于上面的分析,我们先定义一个reducer,用来处理每个action。

reducer.ts


export const dataFetchReducer = (state: any, action: {[type: string]: any}) => {
    switch(action.type) {
        case 'FETCH_INIT':
            return {
                ...state,
                isLoading: true,
                isError: false
            }
        case 'FETCH_SUCCESS':
            return {
                ...state,
                isLoading: false,
                isError: false,
                data: action.payload
            }    
        case 'FETCH_ERROR':
            return {
                ...state,
                isLoading: false,
                isError: true,
                msg: action.payload
            }
        default:
            throw new Error(`Unsupport action type:${action.type}`);
    }
}

上面的reducer非常简单,就是处理上面我们定义的三个action,然后每次都返回一个新的state,解释下上面返回的state的各个字段的用意:

  • isLoading: 是否展示加载中的提示,比如我们请求正在处理,那需要有一个提示信息给到用户
  • isError: 请求是否失败,如果这个值为true的话,那页面就要展示msg中的错误提示信息
  • msg: 错误提示信息,当isError为true的时候不为空

介绍了reducer之后,我们来看下Hook是什么实现的:
代码如下:

interface RequestConfig extends AxiosRequestConfig {
    url: string
}

export const useDataApi = (initData: Array<any> | any, initRequestConfig: RequestConfig) => {
    if (!initRequestConfig.method) {
        initRequestConfig.method = 'get';
    }
    const [requestConfig, setRequestConfig] = useState(initRequestConfig);

    const [state, dispatch] = useReducer(dataFetchReducer, {
        data: initData,
        isLoading: false,
        isError: false
    });
    useEffect(() => {
        const fetchData = async () => {
            try{
                dispatch({
                    type: 'FETCH_INIT'
                });
                if (!requestConfig.url) {
                    dispatch({
                        type: 'FETCH_SUCCESS',
                        payload: []
                    });
                }else {
                    const response = await axios(requestConfig).catch(e => {
                            return e.response;
                        });
                    if (response.data){
                        const data = response.data;
                        const { success, result, message } = data;
                        if (!success) {
                            dispatch({
                                type: 'FETCH_ERROR',
                                payload: message
                            });
                        } else {
                            dispatch({
                                type: 'FETCH_SUCCESS',
                                payload: result
                            });
                        }
                    }else {
                        dispatch({
                            type: 'FETCH_ERROR',
                            msg: '加载数据失败'
                        });
                    }
                }
            }catch(e) {
                dispatch({
                    type: 'FETCH_ERROR',
                    msg: '加载失败'
                });
            }
        };
        fetchData();
    }, [requestConfig]);
    
    return [state, setRequestConfig];
}

在上面的代码中,我们定义了一个自定义的Hooks,名称为useDataApi,这个Hooks有2个参数,第一个参数是表示初始化时候的数据,第二个参数就是我们请求需要用到的各种参数了。

上面的useState我们就不多介绍了,主要来介绍下,下面代码中的useReducer。

const [state, dispatch] = useReducer(dataFetchReducer, {
    data: initData,
    isLoading: false,
    isError: false
});

这里useReducer的第一个参数,就是传入我们刚刚定义好的reducer,那个reducer里面我们处理了3中类型的action,对不对?
第二个参数就是传入一个初始化的satte对象了。
然后看下这里的返回值,他是一个数组,为什么要返回一个数组呢?
因为这样通过解构之后,你可以随意命名他们的两个返回值(一本正经)。

在上面的两个返回值中,第一个state是我们渲染的时候需要用到的,第二个dispatch用来分发action的,这个dispatch分发的actio你,就会被我们自定义的reducer去处理。

然后再来看下下面的useEffect的代码,这里代码比较多,我们一点点看:

 dispatch({
     type: 'FETCH_INIT'
 });

上面的代码主要用来分发一个FETCH_INIT的action,这个时候就回返回isLoading为true的state,组件根据这个来展示loading或者加载中...等提示。

const response = await axios(requestConfig).catch(e => {
    return e.response;
});

这段代码就是具体的发送请求的代码,这里我使用了axios这个库,当然你也可以替换成别的库。

在请求完成之后,需要解析返回的结果,然后来决定是触发FETCH_SUCCESS还是触发FETCH_ERRORaction.

 const data = response.data;
 const { success, result, message } = data;
 if (!success) {
     dispatch({
         type: 'FETCH_ERROR',
         payload: message
     });
 } else {
     dispatch({
         type: 'FETCH_SUCCESS',
         payload: result
     });
 }

这段代码,我们获取response的data,然后解析data的数据,这里data就是后台返回的数据结构了,我这里后台返回的格式就是会包括这三个字段,success标识请求是否成功,result标识请求的结果,message表示提示信息,一般用来返回错误的提示信息。

到这里我们自定义的Hooks就算完成了,然后我们来看下怎么使用这个Hooks来加载数据。

使用自定义的Hooks来加载数据

const [{ isLoading, isError, msg, data }, setRequestConfig] = useDataApi([], {
        url: apiPath
    });
    if (isError) {
        message.error(msg);
    }
    return (
        <div>
            <div className="toolbar">
                <Link to={window.location.pathname + "/detail"}>
                    <Button type="primary" icon='plus'>
                        添加
                    </Button>
                </Link>
            </div>
            <CustomModal />
            <Table columns={columns}
                dataSource={data}
                loading={isLoading}
                rowKey={'id'}
            />
        </div>
    );

看上面的代码,是不是足够简单了?而且你可以再任何地方使用这个自定义的Hooks。

总结

本篇文章主要讲了以下几个点:

  • useEffect和useReducer简单介绍
  • 实现自定义的获取数据的Hooks

AlloyTeam 欢迎优秀的小伙伴加入。
简历投递: alloyteam@qq.com
详情可点击 腾讯AlloyTeam招募Web前端工程师(社招)

clipboard.png


腾讯AlloyTeam
1.7k 声望4.8k 粉丝

AlloyTeam 欢迎优秀的小伙伴加入。