用 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 仍是颇有必要的。

参考

相关文章
相关标签/搜索