React Hooks实现异步请求实例—useReducer、useContext和useEffect代替Redux方案

简介: 本文是学习了2018年新鲜出炉的React Hooks提案之后,针对异步请求数据写的一个案例。注意,本文假设了:1.你已经初步了解hooks的含义了,如果不了解还请移步官方文档。
本文是学习了2018年新鲜出炉的React Hooks提案之后,针对 异步请求数据写的一个案例。注意,本文假设了:
1.你已经初步了解 hooks的含义了,如果不了解还请移步 官方文档。(其实有过翻译的想法,不过印记中文一直在翻译,就是比较慢啦)
2.你使用 Redux实现过异步 Action(非必需,只是本文不涉及该部分知识而直接使用)
3.你听说过 axios或者 fetch(如果没有,那么想象一下原生js的 promise实现异步请求,或者去学习下这俩库)
全部代码参见仓库: github | Marckon选择 hooks-onlineShop分支以及 master分支查看

本文并非最佳实践,如有更好的方法或发现文中纰漏,欢迎指正!

前序方案(不想看可以直接跳过)

  • 不考虑引入Redux

通过学习React生命周期,我们知道适合进行异步请求的地方是componentDidMount钩子函数内。因此,当你不需要考虑状态管理时,以往的方法很简单:


class App extends React.Component{
    componentDidMount(){
        axios.get('/your/api')
            .then(res=>/*...*/)
    }
}
  • 引入Redux进行状态管理

当你决定使用Redux进行状态管理时,比如将异步获取到的数据储存在store中,事情就开始复杂起来了。根据Redux的官方文档案例来看,为了实现异步action,你还得需要一个类似于redux-thunk的第三方库来解析你的异步action

requestAction.js: 定义异步请求action的地方


//这是一个异步action,分发了两个同步action,redux-thunk能够理解它
const fetchGoodsList = url => dispatch => {
    dispatch(requestGoodsList());
    axios.get(url)
        .then(res=>{
            dispatch(receiveGoodsList(res.data))
        })
};

requestReducer.js: 处理同步action


const requestReducer=(state=initialState,action)=>{
    switch (action.type) {
        case REQUEST_GOODSLIST:
            return Object.assign({},state,{
                isFetching: true
            });
        case RECEIVE_GOODSLIST:
            return Object.assign({},state,{
                isFetching:false,
                goodsList:action.goodsList
            });
        default:
            return state;
    }
};

App Component :你引入redux store和redux-thunk中间件的地方


import {Provider} from 'react-redux';
import thunkMiddleWare from 'redux-thunk';
import {createStore,applyMiddleware} from 'redux';
//other imports

let store=createStore(
    rootReducer,
    //这里要使用中间件,才能够完成异步请求
    applyMiddleware(
        thunkMiddleWare,
        myMiddleWare,

    )
);
class App extends React.Component{
    render(){
        return (
            <Provider store={store}>
                <RootComponent/>
            </Provider>
        )
    }
}

GoodsList Component :需要进行异步请求的组件


class GoodsList extends React.Component{
    //...
    componentDidMount(){
        this.props.fetchGoodsList('your/url');
    }
    //...
}
const mapDispatchToProps={
    fetchGoodsList
}
export default connect(
    mapStateToProps,
    mapDispatchToProps
)(GoodsList);

完整代码:branch:master-onlineShop

使用Hooks-useReducer()useContext()

总之使用Redux很累,当然,你可以不使用Redux,直接通过props层层传递,或者使用context都可以。只不过本文我们学过了useReducer,使用到了Redux的思想,总要试着用一下。

这里你不需要引入别的任何第三方库了,简简单单地使用React@16.7.0-alpha.2版本就好啦

很重要的一点就是——函数式组件,现在React推荐我们这么做,可以基本上代替class写法。

函数签名

  1. useReducer(reducer,initialState)
  2. useContext(ctxObj)
  3. useEffect(effectFunction,[dependencyValues])

概览-你需要编写什么

  1. action.js:

    • 我们还使用redux的思想,编写action
  2. reducer.js:

    • 处理action,不同于reduxreducer,这里我们可以不用提供初始状态
  3. 根组件:

    • Provider提供给子组件context
    • useReducer定义的位置,引入一个reducer并且提供初始状态initialState
  4. 子组件:

    • useContext定义的位置,获取祖先组件提供的context
    • useEffect用于进行异步请求

实现

1.action.js:我们使用action创建函数


const REQUEST_GOODSLIST = "REQUEST_GOODSLIST";
const RECEIVE_GOODSLIST = "RECEIVE_GOODSLIST";

//开始请求
const requestGoodsList = () => ({
    type: REQUEST_GOODSLIST
});

//接收到数据
const receiveGoodsList = json => ({
    type: RECEIVE_GOODSLIST,
    goodsList: json.goodsList,
    receivedAt: Date.now()
});

export {
    RECEIVE_GOODSLIST,
    REQUEST_GOODSLIST,
    receiveGoodsList,
    requestGoodsList,
}

2.reducer.js:判断action的类型并进行相应处理,更新state


import {
    RECEIVE_GOODSLIST,
    REQUEST_GOODSLIST,
} from "../..";


export const fetchReducer=(state,action)=>{
    switch (action.type) {
        case REQUEST_GOODSLIST:
            return Object.assign({},state,{
                isFetching: true
            });
        case RECEIVE_GOODSLIST:
            return Object.assign({},state,{
                isFetching:false,
                goodsList:state.goodsList.concat(action.goodsList)
            });
        default:
            return state;
    }
};


3.根组件:引入reducer.js


import React,{useReducer} from 'react';
import {fetchReducer} from '..';

//创建并export上下文
export const FetchesContext = React.createContext(null);

function RootComponent() {
    //第二个参数为state的初始状态
    const [fetchesState, fetchDispatch] = useReducer(fetchReducer, {
            isFetching: false,
            goodsList: []
        });
    return (
        //将dispatch方法和状态都作为context传递给子组件
         <FetchesContext.Provider value={{fetchesState,dispatch:fetchDispatch}}>
             //...
             //用到context的一个子组件
             <ComponentToUseContext/>
         </FetchesContext.Provider>
    )
}

4.子组件:引入FetchesContext


import {FetchesContext} from "../RootComponent";
import React, {useContext, useEffect,useState} from 'react';
import axios from 'axios';

function GoodsList() {

    //获取上下文
    const ctx = useContext(FetchesContext);
    
    //一个判断是否重新获取的state变量
    const [reFetch,setReFetch]=useState(false);

    //具有异步调用副作用的useEffect
    useEffect(() => {
        //首先分发一个开始异步获取数据的action
        ctx.dispatch(requestGoodsList());
            axios.get(proxyGoodsListAPI())
                .then(res=>{
                    //获取到数据后分发一个action,通知reducer更新状态
                    ctx.dispatch(receiveGoodsList(res.data))
                })
      //第二个参数reFetch指的是只有当reFetch变量值改变才重新渲染
    },[reFetch]);

    return (
        <div onScroll={handleScroll}>
            {
                //children
            }
        </div>
    )
}

完整代码参见:branch:hooks-onlineShop

目录结构

我的目录结构大概这样:


src
  |- actions
     |- fetchAction.js
  |- components
     |-...
  |- reducers
     |- fetchReducer.js
  |- index.js

注意点

  1. 使用useContext()时候我们不需要使用Consumer了。但不要忘记exportimport上下文对象
  2. useEffect()可以看做是class写法的componentDidMountcomponentDidUpdate以及componentWillUnMount三个钩子函数的组合。

    • 当返回了一个函数的时候,这个函数就在compnentWillUnMount生命周期调用
    • 默认地,传给useEffect的第一个参数会在每次(包含第一次)数据更新时重新调用
    • 当给useEffect()传入了第二个参数(数组类型)的时候,effect函数会在第一次渲染时调用,其余仅当数组中的任一元素发生改变时才会调用。这相当于我们控制了组件的update生命周期
    • useEffect()第二个数组为空则意味着仅在componentDidMount周期执行一次
  3. 代码仓库里使用了Mock.js拦截api请求以及ant-design第三UI方库。目前代码比较简陋。
来源:https://segmentfault.com/a/1190000017209855
相关文章
|
16天前
|
前端开发 JavaScript
React Hooks 全面解析
【10月更文挑战第11天】React Hooks 是 React 16.8 引入的新特性,允许在函数组件中使用状态和其他 React 特性,简化了状态管理和生命周期管理。本文从基础概念入手,详细介绍了 `useState` 和 `useEffect` 的用法,探讨了常见问题和易错点,并提供了代码示例。通过学习本文,你将更好地理解和使用 Hooks,提升开发效率。
51 4
|
19天前
|
前端开发
深入解析React Hooks:构建高效且可维护的前端应用
本文将带你走进React Hooks的世界,探索这一革新特性如何改变我们构建React组件的方式。通过分析Hooks的核心概念、使用方法和最佳实践,文章旨在帮助你充分利用Hooks来提高开发效率,编写更简洁、更可维护的前端代码。我们将通过实际代码示例,深入了解useState、useEffect等常用Hooks的内部工作原理,并探讨如何自定义Hooks以复用逻辑。
|
4天前
|
前端开发 JavaScript 开发者
“揭秘React Hooks的神秘面纱:如何掌握这些改变游戏规则的超能力以打造无敌前端应用”
【10月更文挑战第25天】React Hooks 自 2018 年推出以来,已成为 React 功能组件的重要组成部分。本文全面解析了 React Hooks 的核心概念,包括 `useState` 和 `useEffect` 的使用方法,并提供了最佳实践,如避免过度使用 Hooks、保持 Hooks 调用顺序一致、使用 `useReducer` 管理复杂状态逻辑、自定义 Hooks 封装复用逻辑等,帮助开发者更高效地使用 Hooks,构建健壮且易于维护的 React 应用。
15 2
|
9天前
|
前端开发 开发者
React 提供的其他重要 Hooks
【10月更文挑战第20天】React 提供了一系列强大的 Hooks,除了 `useRef` 之外,还有许多其他重要的 Hooks,它们共同构成了函数式组件开发的基础。
22 6
|
16天前
|
前端开发 JavaScript 开发者
React Hooks
10月更文挑战第13天
31 1
|
2天前
|
前端开发 JavaScript 中间件
React中使用​​useReducer​​​高阶钩子来管理状态
通过本文的介绍,您可以在React中使用 `useReducer`高阶钩子来管理复杂的状态逻辑。`useReducer`不仅提供了清晰的状态管理方式,还可以通过与 `useContext`结合,实现全局状态管理。此外,通过自定义中间件,可以进一步扩展其功能。希望本文对您理解和应用 `useReducer`有所帮助。
6 0
|
21天前
|
前端开发 JavaScript API
自定义React Hooks综合指南
本文介绍了React Hooks及其在组件开发中的作用,重点讲解了自定义Hook的创建和使用方法。通过实例展示了如何创建`useWindowWidth`、`useFetch`和`useForm`等自定义Hook,并分享了使用自定义Hook的最佳实践。文章强调了自定义Hook在提高代码复用性和组件可维护性方面的重要性。
38 0
|
存储 前端开发 JavaScript
为什么需要React Hooks?
为什么需要React Hooks?
125 0
|
6月前
|
设计模式 前端开发 数据可视化
【第4期】一文了解React UI 组件库
【第4期】一文了解React UI 组件库
345 0
|
6月前
|
资源调度 前端开发 JavaScript
React 的antd-mobile 组件库,嵌套路由
React 的antd-mobile 组件库,嵌套路由
116 0