🌑

Mocha's Blog

React Hooks中Context的使用

发布时间:2021年7月4日

最近在写项目中独立的 npm 组件时,因为不想再引入体积比较大的 Redux/React-Redux 包,所以使用了 Context 来解决跨层级组件之间传值的问题。

Context 是 React 官方提供的跨层级组件数据传递解决方案,通过 Context 及一些 Api 的组合使用可以实现简易版 Redux的功能

在实践中,我们需要用到以下 api / 变量 来实现我们预期的目标:

  • React.createContext - 创建一个 context,并将 Context 以及 Context.Provider wrapper 暴露出去
  • Reducer 函数 - 处理数据处理逻辑
  • InitialState - 用于设置简易版 reducer 中的数据
  • React.useReducer - 接受 Reducer FuncitionInitialState 两个参数,返回 statedispatch
  • React.useContext - 在对应组件中使用后,拿到所有的 state

在介绍完需要使用的一些功能后,我们可以对这些功能提供什么能力进行一个明确的划分

其实仅仅通过 React.useReducer 这个 hook 我们就可以实现创建一个简单的状态管理器的功能,那么 Context 在这其中又扮演着什么角色呢?

首先我们可以明确一下我们的目标,我们的目标是在不同组件中可以拿到完全相同的 state,我们可以将这个目标拆解成两步完成:

  1. 创建一个简单的状态管理器,通过 useReducer 就可以实现
  2. 在不同的组件中拿到状态管理器提供的 state 以及 dispatch 方法

所以我们就可以很清晰地明白,Context 在我们这个需求中,主要扮演者对外传递 state 以及 dispatch 的角色。

可能会有人疑问为什么要提供一个 Context.Provider wrapper,因为在使用 Context 时,只有被 Context.Provider 包裹在内的组件才可以拿到 Context 对外传递的 value

为了实现 调用方 不关心你内部时如何实现的 这个原则,我们会将 Context.Provider 封装成一个父组件,调用方直接将其需要包裹的组件通过 props.children 属性传递过来即可正常使用

我们下面通过代码具体讲解一下整个功能的实现

  1. 创建一个状态管理器

    // 设定我们的初始状态
    const initialState = {
    	count: 1
    }
    
    // 编写我们的 reducer,写法和 redux 的完全相同
    function reducer(state = initialState, action) {
    	const { type, payload } = action || {};
    	
    	switch (type) {
          case 'UPDATE_COUNT':
          	return {
          		...state,
              count: payload
            }
          default:
          	return state;
        }
    }
    
    // 生成 state 以及 dispatch
    const [state, dispatch] = React.useReducer(reducer, initialState);
  2. 创建 Context 并 提供其本身和 Context.Provider wrapper

    // 创建一个 context 并暴露出去
    export const MyContext = React.createContext({});
    
    // 将 wrapper 暴露出去
    export function MyContextWrapper({ children }) {
    	return (
    		<MyContext.Provider value={{ state, dispatch }}>
              {children}
            </MyContext.Provider>
        )
    }

    通过以上两步,我们就实现一个创建了一个简易版的 Redux,那么我们如何将 statedispatch 注入到组件中呢?

在使用Redux时,它提供了 connect 这个高阶函数来完成注入操作。在我们这样的场景下,我们将通过 useContext 这个方法来实现 statedispatch 的注入。

继续看代码

  1. 在入口文件中接入 Context Wrapper

    // index.js
    export default function Entry() {
    	return (
    		<MyContextWrapper>
              <App />
            </MyContextWrapper>
        )
    }

    通过这步操作,我们在 App 组件内部就可以拿到 MyContext 对外传递的 value

  2. 在组件内部获取 Context.value

    // app.js
    export default function App() {
    	const { state, dispatch } = React.useContext(MyContext);
    	
    	const handleClick = React.useCallback(() => {
    		dispatch({
              type: 'UPDATE_COUNT',
              payload: Math.random()
            })
        }, []);
    	
    	return (
    		<div>
              <span>{state.count}</span>
              <button onClick={handleClick}>点击切换</button>
            </div>
        )
    }

    在上面我们可以看到,我们通过 useContext 这个 hook 的使用就可以实现获取 state 以及 dispatch 的逻辑

同样的道理,我们也可以在 Context.Provider Wrapper 包裹过的所有子组件中,通过 useContext 拿到对应的 state 以及 dispatch

总结

其实 createContext 这个 api 已经推出很久了,而且官方也曾经对其也有一次 break update,但在传统的 Class Components 模式开发下其使用率一直不是很高

而在 React Hooks 中,随着 Function ComponentuseReduceruseContext 的出现使得 Context 的可用性以及使用率有了比较明显的提升

当项目中需要统一管理的 state 规模并没有达到一定数量时,完全可以用 Context 来替代掉 React-Redux 来实现快速开发

Powered By Hexo.js Hexo and Minima. Support By Oracle & Docker-Compose.

友情链接: 相随