发布时间:2021年7月4日
最近在写项目中独立的
npm
组件时,因为不想再引入体积比较大的 Redux/React-Redux 包,所以使用了 Context 来解决跨层级组件之间传值的问题。
Context 是 React 官方提供的跨层级组件数据传递解决方案,通过 Context 及一些 Api 的组合使用可以实现简易版 Redux的功能
在实践中,我们需要用到以下 api / 变量 来实现我们预期的目标:
Context
以及 Context.Provider wrapper
暴露出去Reducer Funcition
和 InitialState
两个参数,返回 state
和 dispatch
state
在介绍完需要使用的一些功能后,我们可以对这些功能提供什么能力进行一个明确的划分
其实仅仅通过 React.useReducer
这个 hook 我们就可以实现创建一个简单的状态管理器的功能,那么 Context
在这其中又扮演着什么角色呢?
首先我们可以明确一下我们的目标,我们的目标是在不同组件中可以拿到完全相同的 state
,我们可以将这个目标拆解成两步完成:
useReducer
就可以实现state
以及 dispatch
方法所以我们就可以很清晰地明白,Context
在我们这个需求中,主要扮演者对外传递 state
以及 dispatch
的角色。
可能会有人疑问为什么要提供一个
Context.Provider wrapper
,因为在使用Context
时,只有被Context.Provider
包裹在内的组件才可以拿到Context
对外传递的 value为了实现 调用方 不关心你内部时如何实现的 这个原则,我们会将
Context.Provider
封装成一个父组件,调用方直接将其需要包裹的组件通过props.children
属性传递过来即可正常使用
我们下面通过代码具体讲解一下整个功能的实现
创建一个状态管理器
// 设定我们的初始状态
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);
创建 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
,那么我们如何将 state
和 dispatch
注入到组件中呢?
在使用Redux
时,它提供了 connect
这个高阶函数来完成注入操作。在我们这样的场景下,我们将通过 useContext
这个方法来实现 state
和 dispatch
的注入。
继续看代码
在入口文件中接入 Context Wrapper
// index.js
export default function Entry() {
return (
<MyContextWrapper>
<App />
</MyContextWrapper>
)
}
通过这步操作,我们在 App
组件内部就可以拿到 MyContext
对外传递的 value
在组件内部获取 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 Component
、 useReducer
、 useContext
的出现使得 Context
的可用性以及使用率有了比较明显的提升
当项目中需要统一管理的 state
规模并没有达到一定数量时,完全可以用 Context
来替代掉 React-Redux
来实现快速开发