Redux与Context API的区别,以及我们应该使用哪一个

225 阅读8分钟

Redux与Context API的区别,以及我们应该使用哪一个

引言

Redux学习之旅开始了!最开始我想了解Redux到底是什么。研究一段时间后很快我发现“在研究context时也有类似的功能,两者非常的相近”。所以我整理了一下两者对比分享给大家。


1. 状态管理的必要性

在React开发中一提到state状态管理,useState总是浮现在脑海中。确实,我们可以通过useState来管理所有的state

然而,如果在层级比较多的组件中通过useState管理的state进行数据传递或者共享,情况就会变得复杂。比如某个组件的state发生变化时,其他使用该state的组件也需要知道这个变化,如果向下图这样传递的话显然是非常麻烦的。

image.png 在这种情况下,可能会想到“要不把广泛使用的state集中在一个存储中管理会更方便”。这正是Redux和Context API所做的事情

image.png

2. Redux

2-1. Redux是什么

Redux可以简单地说是用于管理全局state的库。它是React世界中最常用的state管理库。

根据npmtrends的数据显示,粗略估计在React项目中有50-60%使用了Redux。 在这里插入图片描述

2-2. 基本概念

接下来,我将简单介绍Redux是如何管理state的。这里主要是概念,具体的方法和生成方式这里就跳过了。

Store

store全局state保存的存储对象。通过Redux提供的createStore()方法来生成。

为了便于理解,举个简单的例子。假设store中保存了我的银行余额信息的state

{
userName: "小明",
id: "abcd123",
money: 0,
}

如果想要修改这个state,首先需要一个action

Action

action是一个描述对store中保存的state进行什么操作的对象

继续上面的示例。现在余额为0,但我想存入10元。为了做到这一点,需要修改money。这可以在action中描述如下:

{
type: "DEPOSIT_MONEY",
payload: 10,
}

typeaction的名称,描述要执行的操作。这里是存款,所以命名为DEPOSIT_MONEY,金额为10。

还有一个概念叫action creator,它只是一个返回action的函数。将其函数化可以在后续使用中更加方便。

const depositAction = (payload) => {
return {
type: "DEPOSIT_MONEY",
payload,
};
};

存款金额可能会变化,因此使用payload作为参数,使其动态化。

但这并不能直接存款。计算机无法理解人类语言,仅凭DEPOSIT_MONEY无法知道具体要做什么。因此,需要通过reducer来准确指定action的操作。

Reducer

reducer是一个根据actiontype来执行相应操作的函数reducer理解action并更新store中的state,因此需要将actionstate作为参数传入。

根据actiontype,操作会有所不同,因此可以使用switch语句来编写。

const reducer = (state, action) => {
switch (action.type) {
case "DEPOSIT_MONEY":
return {
...state,
money: state.money + action.payload,
};
// 其他情况...
}
};

目前只讨论存款,但实际上还有取款、解约等多种情况,可以将它们全部添加到switch语句的case中。

这样就可以指定action的具体操作,但目前仍然是分散的。接下来需要将action发送到这个reducer,这就是dispatch的作用。

Dispatch

dispatch是将action发送到reducer的过程。

使用react-redux的Hooks useDispatch,可以在组件内返回一个可以进行dispatch的函数。

const dispatch = useDispatch();
// 存款按钮的事件处理程序
const onClickDeposit = (payload) => dispatch(depositAction(payload));

上面的变量payload中传入存款金额。
这样,当点击存款按钮时,存款的action就会被发送到reducer

🙋🏻‍♀️ React-Redux是什么?

React-Redux是一个官方包,通过dispatchaction使React组件能够与Redux store进行交互,从而读取state和更新store

简单来说,它是支持在React中使用Redux的包。如果在React中使用Redux,通常会一起使用React-Redux。

总结
  • store:全局state保存的存储
  • action:描述对store中保存的state进行什么操作的对象
  • action creator:返回action的函数
  • reducer:根据actiontype执行相应操作的函数
  • dispatch:将action发送到reducer

state更新的过程
:当actiondispatch时,reducer操作storestate,所有组件都会反映这个变化。

如果将之前讨论的示例代码的流程可视化,可以得到如下图示。这里借用Redux官网的内容。

image.png

最开始看起来有点复杂,但看了20遍这个gif后,逐渐明白了。(顺便提一下,“Click Event: Deposit”是开始,$10变更是结束。)

3. Context API

3-1. Context API是什么

实际上,React本身也有一个主要的存储。这就是context。在React的官方文档中,context被描述为:

context是为了共享当前认证用户、主题、优先语言等数据而设计的,可以被视为某个React组件树的“全局”数据。

而生成这个context的API就是Context API。

3-2. 基本概念

接下来,我们来简单了解一下Context API的基本概念。

Context

如前所述,context是React本身提供的状态存储管理。可以使用createContext方法生成。

让我们看一个示例代码。假设我们创建了一个管理语言的context

import { createContext } from 'react';
export const LanguageContext = createContext("zh");

通过createContext生成的context对象中包含了ProviderConsumer这两个特殊的React组件。

Provider

Provider的作用是通过value propsstate传递给子组件。在Provider中的所有子组件都可以访问该context内的数据。

import React from 'react';
import LanguageContext from './LanguageContext'; // 上面创建的context
import MainContent from './MainContent'; // 组件
import Header from './Header'; // 组件
export const App = () => {
return (
<LanguageContext.Provider value="zh">
<div className="App">
<Header />
<MainContent />
</div>
</LanguageContext.Provider>
);
}

Provider的value prop是必需的,如果不写就会出现如下错误:

Warning: The value prop is required for the <Context.Provider>. Did you misspell it or forget to pass it?

这样,HeaderMainContent就可以访问context了。通常可以通过Consumer来访问。

Consumer

Consumer的作用是获取Provider传递的state并使用

假设MainContent的代码如下:

Consumer内部的函数中可以使用state。因为我们在Providervalue中传递了zh,所以这个language应该是zh。

虽然这样也可以,但还有更简单的方法。可以使用useContext这个hook。

useContext

useContext的作用与Consumer相同,但它允许我们在不包裹Consumer的情况下获取传递的state

import React, { useContext } from "react";
import LanguageContext from "./LanguageContext";
export const MainContent = () =>
{
const language = useContext(LanguageContext);
return (
<section language={language}>
  // 这里是一些内容
</section>
);
};

这样看起来更简洁。

总结
  • context:React自带的状态存储工具
  • Provider:将state传递给子组件
  • Consumer:获取Provider传递的state并使用
  • useContext:在不包裹Consumer的情况下获取传递的state

state更新的过程
:当更新Providervalue props时,使用ConsumeruseContext的子组件会反映这个变化。

4. Redux与Context API的区别

4-1. React的绑定

Redux是一个库。虽然它常与React一起使用,但也可以在其他框架(如Vue)中使用。

Context API是React内置的功能。显然,只有在React中才能使用。

4-2. 主要存储的数量

Redux的store只有一个。所有的state都由这个单一的store管理。如果想要分开逻辑,可以按功能划分reducer。例如,如果要创建一个Twitter应用,可能会有“用户相关的reducer”、“帖子相关的reducer”等。

Context API可以创建多个context。因此,每个功能都需要生成一个context。在Twitter的例子中,可能会有“用户相关的context”、“帖子相关的context”等。 所以有人认为Context API在功能增加时每次都要生成context会很麻烦。

4-3. 中间件

这是我认为最主要的区别,Redux可以使用中间件

image.png

中间件是什么呢?它可以在action被发送到reducer处理之前,添加其他操作。例如:

  • 在控制台显示action(或在服务器端进行日志记录)
  • 在特定条件下忽略action
  • 在特定action发生时执行JavaScript函数
  • 在特定action发生时触发其他action

等等。中间件通常用于处理异步操作,提供了便利的功能。

而Context API不支持中间件,因此所有的异步处理功能都需要直接实现。

4-4. 复杂性

从基本概念中可以感受到,Redux的概念较多,目录和代码量也会增加,使项目变得复杂。使用Redux时,还需要其他各种库,库增多会导致打包变重。为了解决这个问题,Redux自己开发了redux-toolkit这个库。

相比之下,Context API相对简单。特别是无需安装其他库,只需理解ProvideruseContext即可使用。

5. 结论

5-1. 该使用哪一个

根据之前的区别,可以得出以下结论。

以下场景推荐使用Redux

  • 应用程序规模较大
  • 除了全局state管理功能外,还希望有多样的功能(调试、日志等)
  • 经常需要处理异步操作

以下场景推荐使用Context API

  • 应用程序规模不大
  • 仅需全局state管理功能
  • 不常处理异步操作

虽然Redux的功能比Context API多,但相应的代码量和打包大小也会增加。建议仔细确认优缺点后再做选择


结束

这篇文章……写得很辛苦……📚但通过这篇文章,我终于理解了Redux的结构,可以开始学习redux-saga了。