使用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 }
    });
    
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章