用 useContext + useReducer 替代 redux

Redux 毫无疑问是众多 React 项目首选的状态管理方案,但我觉得 Redux 的开发体验并不好。

比如当你正在开发一个很复杂的功能,中途需要不断添加全局状态,每次添加都不得不重复如下步骤:

  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

以上只是加个状态而已,写很多模板代码还是其次,最要命的是会打断你写代码的思路。

而且随着项目越来越大, redux 的状态树也会变大,维护也会变困难。

useContext + useReducer 如何替代 redux ?

useContextuseReducer 是 React 16.8 引入的新 API。

useContext:可访问全局状态,避免一层层的传递状态。

useReducer:用过 Redux 肯定不会陌生,它主要用于更新复杂逻辑的状态。

下面通过一个简单例子介绍 useContext + useReducer 是如何替代 Redux 的。

代码已放到 codesandbox,查看完整代码

这个例子只有一个功能,点击按钮改变字体颜色。

开始

首先用 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 还是很有必要的。

参考

转载于:https://juejin.im/post/5ceb37c851882520724c7504

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
<span style="font-size:24px;">前言</span><br /> <br /> <span style="font-size:18px;">如释重负,好用的技术就应该越来越简单</span><br /> React Hooks 是 React 16.8 从提案转为正式加入的新特性。这个新特性是个非常棒的设计。 可以说对于React 技术栈的发展具分割线一样的意义。讲师在课程中提到:之前使用 React 作为主要的前端技术,开发一款网页游戏。在整个游戏的各个模块中,Redux ,mobx,以及蚂蚁金服的 ant-design,dva, umi 这些框架或者第三方库都有涉及使用。但是自从了解了Facebook官方提案的 Hooks 特性后,才真正觉得获得了前所未有的解脱。如果你有React开发经验,学习了解 Hooks 后,一定有一种如释重负的轻松感。<br /> <br /> <span style="font-size:18px;">React 带来了方便也带来了迷茫</span><br /> 相信关心 React Hooks 这项新特性的童鞋,很多已经有了一定的 React 开发经验。那么你一定有所体验,React 给我们带来方便的同时,也的确和长久以来的前端开发模式有极大的不同。React 并不需要用继承,而是推荐用嵌套。React 有独特的 jsx 语法。大多数情况 jsx 都使得我们的代码更加简洁了。然而有些时候也给我们带来了一些困扰。 比如数据的传递,逻辑的复用。 react 是一种 mvvm 的设计模式,作为开发者一定要清楚,那些数据是业务数据,那些数据是UI数据。否则你的代码很有可能会陷入混乱局面。<br /> <br /> <span style="font-size:18px;">大型项目中模块化与功能解耦困难</span><br /> 在公司项目中 App 稍大的时候,我们发现状态提升和只通过 props 进行数据传递。很多时候都很难实现我们的需求。这时无论我们是否清楚的了解,但是状态管理也就是 redux mobx 等,轻易地进入到了公司的项目中。我们经过初期的尝试发现状态管理,确实比用纯粹的 React 带来了数据传递上的方便,以及代码组织上的清晰。但前提是你看懂且理解了 redux 大神晦涩的官网文档。 本来 React 被设计用来组件化前端开发。但当我们初期使用状态管理,我们常常会过度的使用状态数据,业务逻辑和ui逻辑没有清楚的分离,最终你的应用代码结果可能是:除了少数几个组件是独立的解耦的,大多数组件都因为状态数据的共享而耦合在了一起,且他们也完全依赖状态管理框架。无法再轻松的转移复用。使用高阶组件,属性渲染,渲染回调等高级特性,确实可以帮我们解决模块或功能的解耦问题。但是这些方法,确实有点超出普通“猿类”的技能。且降低了代码的可读性,对于团队协作,这是很致命的问题。<br /> <br /> <span style="font-size:18px;">React Hooks 真正开启前端模块化的金钥匙</span><br /> 对于以上问题,React Hooks 都有很好的解决方案,官方的设计动机就是解决这些曾经的繁琐,化繁为简。React Hooks 让我们在纯函数中就可以使用 React 的众多特性。而不必使用类。代码扁平,易读。解耦状态相关逻辑,UI逻辑和业务逻辑更好的分离。这些逻辑往往是纯函数,而以前很容易混合在类组件中。通过自定义 Hooks 我们可以把应用中“状态相关”逻辑解耦出来,独立编写到我们自己的hooks 中。从而更加易于复用和独立测试。<br />
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页