用 useContext + useReducer 替代 redux

Redux 毫無疑問是衆多 React 項目首選的狀態管理方案,但我以爲 Redux 的開發體驗並很差。javascript

好比當你正在開發一個很複雜的功能,中途須要不斷添加全局狀態,每次添加都不得不重複以下步驟:css

  1. 去到管理 redux 的文件夾,思考把這個狀態放到狀態樹的哪一個位置,而後新建一個文件夾並命名 myFeature
  2. 建立三個文件 my-feature/actions.jsmy-feature/reducer.jsmy-feature/type.js
  3. combineReducer 和並 reduce
  4. 將 action 引入到組件中
  5. 經過 connect HOC 與你的組件相連
  6. 增長兩個方法 mapStateToProps 和 mapDispatchToProps

以上只是加個狀態而已,寫不少模板代碼仍是其次,最要命的是會打斷你寫代碼的思路。java

並且隨着項目愈來愈大, redux 的狀態樹也會變大,維護也會變困難。react

useContext + useReducer 如何替代 redux ?

useContextuseReducer 是 React 16.8 引入的新 API。git

useContext:可訪問全局狀態,避免一層層的傳遞狀態。github

useReducer:用過 Redux 確定不會陌生,它主要用於更新複雜邏輯的狀態。redux

下面經過一個簡單例子介紹 useContext + useReducer 是如何替代 Redux 的。app

代碼已放到 codesandbox,查看完整代碼dom

這個例子只有一個功能,點擊按鈕改變字體顏色。ide

開始

首先用 create-react-app 建立一個項目,也能夠在 CodeSandbox 上建立一個 React App。

建立顏色展現組件 ShowArea

import React from 'react'

const ShowArea = (props) => {
  return (
    <div style={{color: "blue"}}>字體顏色展現爲blue</div>
  )
}

export default ShowArea
複製代碼

建立按鈕組件 buttons

import React from "react";

const Buttons = props => {
  return (
    <React.Fragment> <button>紅色</button> <button>黃色</button> </React.Fragment> ); }; export default Buttons; 複製代碼

將 ShowArea 和 Buttons 導入 index.js

import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";

import ShowArea from './ShowArea'
import Buttons from './Buttons'

function App() {
  return (
    <div className="App"> <ShowArea /> <Button /> </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement); 複製代碼

狀態管理

很明顯 ShowArea 組件須要一個顏色狀態,因此咱們建立 color.js 來處理狀態。

// color.js
import React, { createContext } from "react";

// 建立 context
export const ColorContext = createContext({});

/** * 建立一個 Color 組件 * Color 組件包裹的全部子組件均可以經過調用 ColorContext 訪問到 value */
export const Color = props => {
  return (
    <ColorContext.Provider value={{ color: "blue" }}> {props.children} </ColorContext.Provider> ); }; 複製代碼

引入狀態

修改 index.js,讓全部子組件均可以訪問到顏色。

// index.js
···
···
···
import { Color } from "./color";

function App() {
  return (
    <div className="App"> <Color> <ShowArea /> <Buttons /> </Color> </div>
  );
}
···
···
···
複製代碼

獲取狀態

在 ShowArea 組件中獲取顏色

// ShowArea.js
···
···
···
import { ColorContext } from "./color";

const ShowArea = props => {
  const { color } = useContext(ColorContext);
  return <div style={{ color: color }}>字體顏色展現爲{color}</div>;
};
···
···
···
複製代碼

建立 reducer

接着在 color.js 中添加 reducer, 用於處理顏色更新的邏輯。

import React, { createContext, useReducer } from "react";

// 建立 context
export const ColorContext = createContext({});

// reducer
export const UPDATE_COLOR = "UPDATE_COLOR"
const reducer = (state, action) => {
  switch(action.type) {
    case UPDATE_COLOR:
      return action.color
    default:
      return state  
  }
}

/** * 建立一個 Color 組件 * Color 組件包裹的全部組件均可以訪問到 value */
export const Color = props => {
  const [color, dispatch] = useReducer(reducer, 'blue')
  return (
    <ColorContext.Provider value={{color, dispatch}}> {props.children} </ColorContext.Provider> ); }; 複製代碼

更新狀態

爲按鈕添加點擊事件,調用 dispatch 就能夠更新顏色了。

// buttons.js

import React, { useContext } from "react";
import { colorContext, UPDATE_COLOR } from "./color";

const Buttons = props => {
  const { dispatch } = useContext(colorContext);
  return (
    <React.Fragment> <button onClick={() => { dispatch({ type: UPDATE_COLOR, color: "red" }); }} > 紅色 </button> <button onClick={() => { dispatch({ type: UPDATE_COLOR, color: "yellow" }); }} > 黃色 </button> </React.Fragment> ); }; export default Buttons; 複製代碼

總結

  • useContext 建立全局狀態,不用一層一層的傳遞狀態。
  • useReducer 建立 reducer 根據不一樣的 dispatch 更新 state。
  • 代碼寫到哪裏狀態就加到哪裏,不用打斷思路跳到 redux 裏面去寫。
  • 全局狀態分離,避免項目變大致使 Redux 狀態樹難以管理。

因此 useContext + useReducer 徹底能夠替代 React 進行狀態管理。可是目前絕大多數 React 項目仍在使用 Redux,因此深刻學習 Redux 仍是頗有必要的。

參考

相關文章
相關標籤/搜索