使用React-hooks 和 context api重写todomvc
简介
本文演示了如何使用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 } });