乐趣区

关于react.js:如何采用React-Context-Hooks-去做全局状态管理

在业务或者 UI 交互略微简单一些的我的项目里,都离不开状态治理的问题。不论是从后盾 API 申请的数据还是页面的 UI 状态,都须要有一个 ”Store” 帮咱们去做状态治理。通常在我的项目中,咱们会引入 Redux 去负责这样的职责。然而 Redux 要保护大量的模板代码,加上 Redux 通过 connect 这种高阶组件的形式注入 state 和 dispatch 的形式并不直观,减少了了解的复杂度。React 从 16.8 开始,引入了 React Hook,配合 Context API,能够在我的项目中来解决全局状态治理的问题。

React Context + Hooks 状态治理架构


咱们先来整体看一下通过 React Context 和 Hooks 进行状态治理的架构图。咱们把形成架构的每个负责不同职责的节点称为 ” 性能单元 ”,上面列出了架构图中波及的所有性能单元和对应的职责:

  • React Context:负责全局状态治理,通过提供的 Context API,能够进行状态的读写。每个 Context 都会生成 Context.Provider 和 Context.Consumer

    • Context.Provider:为生产组件提供存储的 context 值,通常存储全局 state 和用来散发 action 的 dispatch 办法
    • Context.Consumer:能够订阅 context 的变更,引入 Hooks 后,能够应用 useContext 来读取 context,因而在实现中不须要关怀 Context.Consumer
  • Component:组件的概念不须要多讲,在基于组件的架构中,是用来形成 React 利用最根底的性能单元
  • Action:Action 是组件收回的告诉,用于形容要产生的事件或者变动
  • Reducer:用于响应 Action,来解决 state 的变动,基于 Action 和旧的 state 返回新的 state

在架构图中,形容性能单元之间的交互的 ”S” 和 ”D”,别离示意 state 和 dispatch 办法,其中

  • dispatch:是组件触发 Action 的惟一办法

相熟 Redux 的人并不会对 Action 和 Reducer,以及 dispatch 的概念生疏,咱们也能从上述职责形容中看出,不论是在 Redux 还是 React Hooks 中,这几个性能单元的职责是雷同的,React 引入这些性能单元,也是为了解决各种简单状态逻辑的场景。这里,奇妙的应用了 Context API 进行全局状态治理,应用 Hooks 提供的 useReducer 来去解决组件状态的变动,既使得数据流向变得清晰、可预测,又防止 Redux 简单的模板代码生成和数据流治理。接下来,咱们来通过一个例子看一下代码的具体实现。

举例

咱们以游客在页面查看评论列表为例,来应用 React Context + Hooks 实现评论数据的全局状态治理

创立 React Context

咱们须要在 React App 中创立一个 context 对象来存储评论列表数据。基于架构咱们晓得,React Context 通过 Context.Provider 来为生产组件提供存储的 context,所以这里咱们先创立一个 <CommentProvider /> 来返回 Context.Provider 和 Context 对象自身。

// CommentProvider.js
const CommentContext = createContext({});

const CommentProvider = ({children}) => {const [state, dispatch] = useReducer(commentReducer, { comments: [] });
    return (<CommentContext.Provider value={{ state, dispatch}}> 
            {children} 
        </CommentContext.Provider> 
    );
};

<CommentProvider/> 中,调用 useReducer Hook 来去注册 commentReducer(会在前面创立),将解构的 state 和 dispatch 函数通过 CommentContext.Provider 提供给子组件。

以后是将 state 和 dispatch 未通过加工间接提供给了 Provider,很多实现会在 Provider 中解构 state,只去传递 <CommentProvider/> 须要的 state,另外也会将组装好的 Actions 间接传递给生产组件。尽管这种形式更明确要传递的数据和办法,然而解构 state 和 创立 Actions 的逻辑因为封装在 Provider 中,导致很难测试。所以,这里还是借鉴了 Redux 的形式,在独立的 Action Creator 去定义 Action,而后在组件中间接调用。

通过 CommentProvider 来组装 CommentList 组件

构建好 <CommentProvider/> 当前,在 <App/> 中,便能够通过 <CommentProvider/> 来组装 <CommentList /> 了。

// App.js
const App = () => ( 
    <CommentProvider> 
        <CommentList /> 
    </CommentProvider>
);

接下来,咱们来看一下,如何在 <CommentList /> 中去应用 Context.Provider 提供的 context 值。

创立 CommentList 组件

// CommentList.js
import {fetchComments} from '../commentAction';

function CommentList() {const { state, dispatch} = useContext(CommentContext);
    
    useEffect(() => {fetchComments(dispatch); 
    }, []);
 
    return (<ul> 
        {state.comments.map((comment) => (<li key={comment.id}><p>{comment.body}</p><span>{comment.name}</span></li> 
        ))}
    </ul>);
}

相熟 React 的话应该分明,通常在 useEffect Hook 中去解决 API 申请,来获取评论列表数据。在未应用 Context 对数据进行治理时,在组件内会间接通过组件内 state 来存取列表数据,而后触发组件的从新渲染。这里不同的是,咱们通过 useContext 获取了全局的 state 数据,而后在 useEffect Hook 中,调用了 fetchComments Action,并没有间接在组件内调用 setState。咱们在架构图中跟踪 “S” 的变动,能够看到,调用 Action 后,Reducer 会对 state 进行更新,并将新的 state 值更新到 context 中。那咱们就来看一下,如何创立 Action 和 Reducer 来更新 state 中评论列表的值。

构建 commentAction

// commentAction.js
const fetchComments = async (dispatch) => {const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1/comments');
    dispatch({type: 'SET_COMMENTS', payload: response.data});
}

当异步获取评论列表数据后,便通过传入的 dispatch 办法来去收回一个更新评论列表的 ‘SET_COMMENTS’ 的 Action,commentReducer 在接管到这个 Action 当前,便会从
payload 中取出传递的评论列表数据,而后更新到 context 的全局状态中。

构建 commentReducer

// commentReducer.js
const commentReducer = (state, action) => {if (action.type === 'SET_COMMENTS') {return { ...state, comments: action.payload}; 
    }
    return state;
};

须要留神的是,和 Redux 强调的统一,reducer 是一个纯函数,只依据传入的 Action 和 旧的 state,返回一个新的 state 值。context 拿到新的 state 后,便会对旧的 state 进行替换。到此,一个残缺的更新评论列表的数据流曾经走完。

总结

咱们借用 React 引入的 Context 来去进行状态治理,通过 useReducer Hook 来去更新 state 的变动,并且借鉴了 Redux 中 Action 的形象,来去形容组件中产生的事件或者变动。这种形式对全局状态的读取和更新进行了隔离,使得数据在性能单元间的流向更加清晰、易保护。

在理论我的项目中,须要留神 Context.Provider 中的嵌套关系,须要在适合的父节点提供 context 的值。能够通过 UI 或业务组件和状态之间的业务关系来去梳理。

能够在 https://github.com/dujuanxian/react-context-with-hook-comments-demo 中查看代码的实现,其中除了查看评论列表的性能,还蕴含创立评论的性能。

更多文章,请查看我的博客 dujuan.in

退出移动版