Redux与Context API的区别,以及我们应该使用哪一个
引言
Redux学习之旅开始了!最开始我想了解Redux到底是什么。研究一段时间后很快我发现“在研究context时也有类似的功能,两者非常的相近”。所以我整理了一下两者对比分享给大家。
1. 状态管理的必要性
在React开发中一提到state状态管理,useState总是浮现在脑海中。确实,我们可以通过useState来管理所有的state。
然而,如果在层级比较多的组件中通过useState管理的state进行数据传递或者共享,情况就会变得复杂。比如某个组件的state发生变化时,其他使用该state的组件也需要知道这个变化,如果向下图这样传递的话显然是非常麻烦的。
在这种情况下,可能会想到“要不把广泛使用的
state集中在一个存储中管理会更方便”。这正是Redux和Context API所做的事情。
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,
}
type是action的名称,描述要执行的操作。这里是存款,所以命名为DEPOSIT_MONEY,金额为10。
还有一个概念叫action creator,它只是一个返回action的函数。将其函数化可以在后续使用中更加方便。
const depositAction = (payload) => {
return {
type: "DEPOSIT_MONEY",
payload,
};
};
存款金额可能会变化,因此使用payload作为参数,使其动态化。
但这并不能直接存款。计算机无法理解人类语言,仅凭DEPOSIT_MONEY无法知道具体要做什么。因此,需要通过reducer来准确指定action的操作。
Reducer
reducer是一个根据action的type来执行相应操作的函数。reducer理解action并更新store中的state,因此需要将action和state作为参数传入。
根据action的type,操作会有所不同,因此可以使用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是一个官方包,通过dispatch
action使React组件能够与Redux store进行交互,从而读取state和更新store。
简单来说,它是支持在React中使用Redux的包。如果在React中使用Redux,通常会一起使用React-Redux。
总结
- store:全局
state保存的存储 - action:描述对
store中保存的state进行什么操作的对象 - action creator:返回
action的函数 - reducer:根据
action的type执行相应操作的函数 - dispatch:将
action发送到reducer
state更新的过程
:当action被dispatch时,reducer操作store的state,所有组件都会反映这个变化。
如果将之前讨论的示例代码的流程可视化,可以得到如下图示。这里借用Redux官网的内容。
最开始看起来有点复杂,但看了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对象中包含了Provider和Consumer这两个特殊的React组件。
Provider
Provider的作用是通过value props将state传递给子组件。在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
valueprop is required for the<Context.Provider>. Did you misspell it or forget to pass it?
这样,Header和MainContent就可以访问context了。通常可以通过Consumer来访问。
Consumer
Consumer的作用是获取Provider传递的state并使用。
假设MainContent的代码如下:
在Consumer内部的函数中可以使用state。因为我们在Provider的value中传递了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更新的过程
:当更新Provider的value props时,使用Consumer或useContext的子组件会反映这个变化。
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可以使用中间件。
中间件是什么呢?它可以在action被发送到reducer处理之前,添加其他操作。例如:
- 在控制台显示
action(或在服务器端进行日志记录) - 在特定条件下忽略
action - 在特定
action发生时执行JavaScript函数 - 在特定
action发生时触发其他action
等等。中间件通常用于处理异步操作,提供了便利的功能。
而Context API不支持中间件,因此所有的异步处理功能都需要直接实现。
4-4. 复杂性
从基本概念中可以感受到,Redux的概念较多,目录和代码量也会增加,使项目变得复杂。使用Redux时,还需要其他各种库,库增多会导致打包变重。为了解决这个问题,Redux自己开发了redux-toolkit这个库。
相比之下,Context API相对简单。特别是无需安装其他库,只需理解Provider和useContext即可使用。
5. 结论
5-1. 该使用哪一个
根据之前的区别,可以得出以下结论。
以下场景推荐使用Redux
- 应用程序规模较大
- 除了全局
state管理功能外,还希望有多样的功能(调试、日志等) - 经常需要处理异步操作
以下场景推荐使用Context API
- 应用程序规模不大
- 仅需全局
state管理功能 - 不常处理异步操作
虽然Redux的功能比Context API多,但相应的代码量和打包大小也会增加。建议仔细确认优缺点后再做选择。
结束
这篇文章……写得很辛苦……📚但通过这篇文章,我终于理解了Redux的结构,可以开始学习redux-saga了。