簡介
本文演示瞭如何使用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 }
});