简介
本文演示了如何使用React hooks和context 实现简单简单的data store和状态管理. 假设你对以下内容有基本了解(如果没有请google):
- react-hooks
- react context
- todomvc
- redux
状态管理
当提到状态管理我们首先都会想到Redux,在react项目中这几乎已经成为事实标准。Redux的优点无需多说,然而很多时候像Redux的作者说的一样,你可能不需要Redux。 在某些场景中可能context api就可以满足你的需求。在实际使用中,你可能发现很多地方和Redux的设计不谋而合,实际上Redux底层也使用了Context Api.
闲话少说,开始上代码!
代码结构
├── TodoContext.jsx // context
├── TodoProvider.js // context provider
├── components // react components
├── index.jsx
├── stores
│ ├── reducer.js // data store reducer
│ └── util.js
└── useTodo.js // customer hooks
React Context API
为了实现状态管理,我们创建一个context来保存todo数据的状态:
- TodoContext
export const TodoContext = React.createContext(undefined);
这样我们就可以在我们的React组件中使用它,这里假设我们有一个todoState
对象,下面会讲到如何生成它:
const App = {children} => (
<TodoContext.Provider value={todoState}>
{children}
</TodoContext.Provider>
);
现在我们创建了Context,在组件中也引入了,那么如何在子组件中使用context,以及如何传入初始状态,和修改状态呢? 我们来看一下如何结合React Hooks的方法来使用context。
React Hooks
首先我们来封装一个Provider对象,这个provider对象接受一个useReducer的执行结果,也就是上文提到的todoState对象。可以理解为一个[data, dispatcher]
.这里的state和dispatcher的概念和Redux中非常相似。
- TodoProvider
export const TodoProvider = ({ reducer, initialState, children }) => (
<TodoContext.Provider value={useReducer(reducer, initialState)}>
{children}
</TodoContext.Provider>
);
- useReducer
看到这里越来越熟悉,这不就是redux中的reducer嘛 ? 没错,reducer的作用就是根据不同的action和payload的组合,更新state中的数据。
export const reducer = (state, action) => {
const { id, text } = action.payload || { id: undefined, text: undefined };
const { todos, visibilityFilter } = state;
switch (action.type) {
case "ADD_TODO":
return {
todos: [
{
id: Math.random()
.toString(16)
.substring(2),
completed: false,
text
},
...todos
],
visibilityFilter
};
case "DELETE_TODO":
return {
todos: todos.filter(todo => todo.id !== id),
visibilityFilter
};
// ....... 略
有了TodoProvider,现在App.tsx中引用方式变成如下,同时我们在这里传入了initState。
const initialState = {
todos: [
{
text: "React Hooks",
completed: false,
id: 0
},
{
text: "Context",
completed: true,
id: 1
}
],
visibilityFilter: "All"
};
const App = () => (
<TodoProvider initialState={initialState} reducer={reducer}>
<div>
<Header />
<MainSection />
</div>
</TodoProvider>
);
- useTodo
有了state和reducer方法,怎么在组件中使用他们呢? 换句redux的话说如何把state和actionDispatcher和组件connect起来? 答案是useContext!这里我们创建了一个custom hooks,任何使用我们想使用todoState的时候,可以直接使用useTodo。
export const useTodo = () => useContext(TodoContext);
在组件中的用法:
// 引入
const [{ todos, visibilityFilter }, dispatch] = useTodo();
// 创建新TODO
dispatch({
type: "ADD_TODO",
payload: { text }
});