使用React-hooks 和 context api重寫todomvc

簡介

本文演示瞭如何使用React hooks和context 實現簡單簡單的data store和狀態管理. 假設你對如下內容有基本瞭解(若是沒有請google):html

  • react-hooks
  • react context
  • todomvc
  • redux

狀態管理

當提到狀態管理咱們首先都會想到Redux,在react項目中這幾乎已經成爲事實標準。Redux的優勢無需多說,然而不少時候像Redux的做者說的同樣,你可能不須要Redux。 在某些場景中可能context api就能夠知足你的需求。在實際使用中,你可能發現不少地方和Redux的設計不謀而合,實際上Redux底層也使用了Context Api.react

閒話少說,開始上代碼!redux

代碼結構

├── 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數據的狀態:api

  • TodoContext
export const TodoContext = React.createContext(undefined);

這樣咱們就能夠在咱們的React組件中使用它,這裏假設咱們有一個todoState對象,下面會講到如何生成它:mvc

const App = {children} => (
  <TodoContext.Provider value={todoState}>
    {children}
  </TodoContext.Provider>
);

如今咱們建立了Context,在組件中也引入了,那麼如何在子組件中使用context,以及如何傳入初始狀態,和修改狀態呢? 咱們來看一下如何結合React Hooks的方法來使用context。dom

React Hooks

首先咱們來封裝一個Provider對象,這個provider對象接受一個useReducer的執行結果,也就是上文提到的todoState對象。能夠理解爲一個[data, dispatcher].這裏的state和dispatcher的概念和Redux中很是類似。ide

  • TodoProvider
export const TodoProvider = ({ reducer, initialState, children }) => (
    <TodoContext.Provider value={useReducer(reducer, initialState)}>
        {children}
    </TodoContext.Provider>
);
  • useReducer

看到這裏愈來愈熟悉,這不就是redux中的reducer嘛 ? 沒錯,reducer的做用就是根據不一樣的action和payload的組合,更新state中的數據。google

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。spa

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 }
    });
相關文章
相關標籤/搜索