React Hooks useContext + useReducer實現簡易Redux


context api是簡化版的redux,他沒有redux強大生態體系,結合各自中間件例如thunk或saga,做data fetching或處理side effect,不過單單想存一些share data避免props drilling的問題卻綽綽有余。

  • context 提供了一個無需為每層組件手動添加 props,就能在組件樹間進行數據傳遞的方法
  • reducer 應是純函數,根據舊的狀態和新的參數計算出最新的狀態,其中新的參數來自於 dispatch(新的參數)

所以使用 context 還是 redux 要看需求、看成本、看以后拓展性、可維護性。(react-redux 從 v7.1.0開始也提供了 hooks api 以減少繁瑣的高階組件嵌套)

跟着 React 官網文檔,首先去看 高級指引-Context,重點理解 React.createContext(initialValue) 方法返回的 Provider 和 Consumer 組件,這是生產者和消費者模型。

知道 context 在函數式組件中基本用法后,去看 useContext,重點去感受使用了 useContext 后,代替了之前 <MyContext.Consumer> 這種寫法,令 Context 的引用變得更加方便。

接下來假設你本身已經使用過 Redux,對 Redux 那一套流程有基本的了解,Redux 本身是可以脫離 React 運行的,這是個用於全局狀態管理的庫,你當然可以結合 JQuery 來使用,但目前來看,之所以這么流行,還是因為結合了 React,所以 Redux 在 React 中主要依賴了 react-redux 這個庫。

跟着 React 官方文檔,了解 useReducer 的用法,這里也提供了 useReducer 的大概原理。

相信我,盡管可能先從其他地方開始學習 hooks,但是官方文檔絕對不要錯過。

我們模擬類似 redux 的功能,簡單分為三步:

  1. 使用 useReducer 在根組件創建需要共享的 state 和用來更新它的 dispatch()
  2. 使用 context api 把剛才的 state 和 dispatch 同時共享下去
  3. 使用 useContext 方便底層組件讀寫共享的 state
import React, { useContext, useReducer } from 'react';

const BankContext = React.createContext({});

// 和 redux 一樣,綜合根據舊 state 和 dispatch 而來的數據,計算出最新的 state
function reducer(state, action) {
  switch (action.type) {
    case 'deposit':
      return { balance: state.balance + action.payload };
    default:
      throw new Error();
  }
}

// 純粹為了展示 useReducer 第三個參數
function init(initialCount) {
  return { balance: initialCount };
}

// 根組件
export default function App() {
  const [state, dispatch] = useReducer(reducer, 0, init);
  return (
    <BankContext.Provider value={{
      state,
      dispatch // 把 dispatch 也作為 context 的一部分共享下去,從而在嵌套組件中調用以實現更新頂層的 state
    }}>
      <Layout>
        <Content />
      </Layout>
    </BankContext.Provider>
  );
}

// 子組件
function Layout(props) {
  return (
    <div style={{ border: '5px solid lightblue', padding: '20px' }}>
      <p>您就當我是個 Layout 組件吧!</p>
      {props.children}
    </div>
  );
}

// 孫組件
// 經過層層嵌套后,可以在孫組件中讀取全局 state 並設置
function Content() {
  // 這里不要誤會,useContext(BankContext) 返回值就是我們共享出來的 context,
  // 只是這里刻意把 context 設計為對象,以便同時提供 dispatch
  const { state, dispatch } = useContext(BankContext);
  return (
    <div style={{ border: '1px solid #666' }}>
      <div> 當前余額:{state.balance}</div>
      <button onClick={() => dispatch({ type: 'deposit', payload: 100 })}>存入100元</button>
    </div>
  );
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM