Redux 状态容器、管理

SEO教程2025-07-3070

当你越来越有能力时,自然会有人看得起你;改变自己,你才有自信,梦想才会慢慢的实现。喷泉之所以漂亮是因为她有了压力;瀑布之所以壮观是因为她没有了退路;水之所以能穿石是因为永远在坚持。

首先我们要明确一个 React 组件,它与数据挂钩的只有 props 和 state,一个是从上级传下来的数据,一个是内部的状态,只能向下传,不能直接向上传。

这样的话,我们如何处理同级别组件的通信呢?一个最直接的方式就是创建一个最顶层的 state,把数据当作 props 向下传。这个最顶层的存放 state 的地方,我们可以认为是 store。

1. Flux 和 Redux

Flux 是一种架构思想,专门解决软件的结构问题,它跟 MVC 架构是同一类东西,但是更加简单和清晰。Flux 存在至少多种实现,比如:Redux。

https://github.com/voronianski/flux-comparison

Facebook Flux 是用来构建客户端 Web 应用的应用架构。利用单向数据流的方式来组合 React 中的视图组件。它更像一个模式而不是一个正式的框架,开发者不需要太多的新代码就可以快速的上手 Flux。

中心思想流程:

  • 用户访问 View,View 订阅 Store;
  • View 发出用户的 Action;
  • Dispatcher 收到 Action,要求 Store 进行相应的更新;
  • Store 更新后,发出一个 Change 事件;
  • View 收到 Change 事件后,更新页面;

5. Flux.png

Redux 最主要是用作应用状态的管理。简言之,Redux 用一个单独的常量状态树(state对象),保存这一整个应用的状态,这个对象不能直接被改变,当一些数据变化了,一个新的对象就会被创建(使用 actions 和 reducers)这样就可以进行数据追踪,实现时光旅行。

2. Redux 工作流

Redux 原理就是订阅、发布模式。我们看其中主要的几个重要的概念:

  • 组件 Component
  • Actions:用户系统行为,Actions Creators 创建;
  • Store:state 以单一对象存储在 store 中:
    • 状态调度:Store.dispatch(action);
    • 状态展示:Store.getState();
    • 订阅改变: Store.subscribe();
  • Reducers:纯处理函数 reducer,里面对老状态处理,得到新状态;

6. Redux 工作流.png

点击组件按钮触发一个事件,通过 Action Creator 创建一个 action 对象。通过 dispatch 把这个 action 对象发送到 store 里面。在 store 里面 需要通过 reducers 来更新状态。store 自己是无法更新状态的。reducers 必须接收老的对象和 action,然后根据 action 的 type 不同进行处理,返回新的状态,新的状态更新了,store 就会通知那些订阅者组件进行更新。

Redux store 默认将数据存储在内存中,因此‌

页面刷新后数据会丢失

‌。这是因为浏览器刷新会重新加载整个应用,内存中的状态会被重置‌。

Action 文件:

var increment = () => { return { type: "increment" } } var decrement = () => { return { type: "decrement" } } export { increment, decrement } 

Store 文件:

import { createStore } from "@reduxjs/toolkit"; import counterReducer from "./reducer"; const Store = createStore(counterReducer) export default Store 

Reducer 文件:

const counterReducer = (state, action) => { if (state === undefined) { return 0 } switch (action.type) { case "increment": { let newState = state newState += 1 return newState } case "decrement":{ let newState = state newState -= 1 return newState } default: return state } } export default counterReducer 

应用示例:

import React, { useEffect, useState } from 'react' import Store from './1-AppStore/store' import { increment, decrement } from './1-AppStore/actions' export default function ReduxBaseJs() { const [count, setcount] = useState(Store.getState()) useEffect(()=>{ Store.subscribe(() => { console.log(Store.getState()) setcount(Store.getState()) }) }) return ( <div> <div> <button aria-label="Increment value" onClick={() => Store.dispatch(increment())} > Increment </button> <span>{count}</span> <button aria-label="Decrement value" onClick={() => Store.dispatch(decrement())} > Decrement </button> </div> </div> ) } 

3. Redux 使用三大原则

  • state 以单一对象存储在 store 对象中;
  • state 只读,每次更新都是返回一个新的对象;
  • 使用纯函数 reducer 执行 state 更新;
    • 对外界没有副作用。即调用后对外界变量、对象没有影响。
    • 同样的输入得到同样的输出。

4. 你需要使用 Redux 吗?

虽然 Redux 是一个很有价值的管理状态工具,但还是要考虑下它是否适合你的场景。

不要仅仅因为有人说过应该使用 Redux 而使用 - 应该花一些时间来了解使用它的潜在好处和取舍

。当遇到如下问题时,建议开始使用 Redux:

  • 你有很多数据随时间而变化;
  • 你希望状态有一个唯一确定的来源(single source of truth);
  • 你发现将所有状态放在顶层组件中管理已不可维护;

5. Redux 使用之 Reducer 函数拆分 combineReducers

应用的整体全局状态以对象树的方式存放于单个 store。 唯一改变状态树(state tree)的方法是创建 action,一个描述发生了什么的对象,并将其 dispatch 给 store。 要指定状态树如何响应 action 来进行更新,你可以编写纯 reducer 函数,这些函数根据旧 state 和 action 来计算新 state。

Redux 扩展(行为拆分),如果不同的 Action 所处理的属性之间没有联系,我们可以把 Reducer 函数拆分。不同的函数负责处理不同属性,最終把它们合并成一个大的 Reducer 即可。那在 dispatch(action) 的时候是怎么知道用那个 reducer 来处理的呢?所有的 reducer 都会执行一遍,其实就是所有的 reducer 轮询匹配。

store.js 文件:

// 创建合并 Reducer import {combineReducers} from "redux"; const reducer = combineReducers ({ aReducer, bReducer, cReducer }) const store = createStore(reducer) 

访问:

// 访问: store.getState.aReducer.property // 不同的命名空间 

TS 示例:

reducer.ts 文件:

// 定义一个 Reducer 纯函数 const counterReducer = (state: any, action: any) => { if (state === undefined) { return 0 } switch (action.type) { case "increment": { let newState = state newState += 1 return newState } case "decrement":{ let newState = state newState -= 1 return newState } default: return state } } export default counterReducer 

actions.ts 文件:

// 定义相关行为 action var increment = () => { return { type: "increment" } } var decrement = () => { return { type: "decrement" } } export { increment, decrement } 

store.ts 文件:

import { createStore } from "@reduxjs/toolkit"; import counterReducer from "./reducer"; const Store = createStore(counterReducer) export default Store 

component.ts 文件:

import React, { useEffect, useState } from 'react' import Store from './2-AppStore/store' import { increment, decrement } from './2-AppStore/actions' export default function ReduxBaseTs() { const [count, setcount] = useState(Store.getState()) useEffect(() => { Store.subscribe(() => { console.log(Store.getState()) setcount(Store.getState()) }) }) return ( <div> <div> <button aria-label="Increment value" onClick={() => Store.dispatch(increment())} > Increment </button> <span>{count}</span> <button aria-label="Decrement value" onClick={() => Store.dispatch(decrement())} > Decrement </button> </div> </div> ) } 

⚠️:以上基础使用方法已经不是官方推荐的编写方式了,现在推荐使用 Redux Toolkit 编写 Redux 逻辑的方法。

6. React-redux

其实 Redux 和 React 没有任何关系,它是基于 Flux 实现的一套可用于 React 状态管理的库。而 React-redux 是基于 Redux 库(必须引入、依赖 Redux),在 Redux 基础上多了一点 React 特性。帮你构建父组件以及订阅和发布这样的一些事情。这样在 React 中状态管理使用更加方便。

React-redux 在 Redux 基础上,通过 connect 高阶函数生成高阶组件(父组件)包装订阅、发布功能(帮你订阅和取消订阅),不用开发者自己发起订阅和发布。其次通过最外层 Provider 供应商组件负责把 store 跨级给 connect 组件,原理就是通过 context 一级一级将 store 传递给 connect 组件。即通过 connect 包装就将 App UI 组件变成了容器组件,之前的组件就变成了 UI 组件。具体应用参考 Redux 中文官网。

https://github.com/reduxjs/react-redux

Redux Toolkit 一般在 React 项目中结合 React-redux 使用。

》UI 组件与容器组件:

1)UI 组件

  • 只负责 UI 的呈现,不带有任何业务逻辑;
  • 没有状态(即不使用 this.state 这个变量),所有数据都由参数(this.props)提供;
  • 不使用任何 Redux 的 API;

2)容器组件

  • 负责管理数据和业务逻辑,不负责 UI 的呈现;
  • 带有内部状态;
  • 使用 Redux 的 API;

》高阶组件(HOC:Higher order components) 与 context 通信在 react-redux 底层中的应用:

  • connect 是 HOC,高阶组件;
  • Provider 组件,可以让容器组件拿到 state,使用了context;

高阶组件构建与应用:

HOC 不仅仅是一个方法,确切说应该是一个组件工厂,获取低阶组件,生成高阶组件。

  • 代码复用,代码模块化;
  • 增删改 props;
  • 渲染劫持;

7. Redux ToolKit

Redux Toolkit 简化了编写 Redux 逻辑和设置 store 的过程。 使用 Redux Toolkit,相同的示例逻辑如下所示。更详细使用可以参考 Demo 工程和 Redux 中文官网。

相关 API:

  • createSlice;
  • configureStore;

TS 示例:

import { createSlice, configureStore } from '@reduxjs/toolkit' const counterSlice = createSlice({ name: 'counter', initialState: { value: 0 }, reducers: { incremented: state => { // Redux Toolkit 允许在 reducers 中编写 "mutating" 逻辑。 // 它实际上并没有改变 state,因为使用的是 Immer 库,检测到“草稿 state”的变化并产生一个全新的 // 基于这些更改的不可变的 state。 state.value += 1 }, decremented: state => { state.value -= 1 } } }) export const { incremented, decremented } = counterSlice.actions const store = configureStore({ reducer: counterSlice.reducer }) // 可以订阅 store store.subscribe(() => console.log(store.getState())) // 将我们所创建的 action 对象传递给 `dispatch` store.dispatch(incremented()) // {value: 1} store.dispatch(incremented()) // {value: 2} store.dispatch(decremented()) // {value: 1} 

8. Redux 中间件

在 redux 中,action 仅仅是携带了数据的普通 js 对象。action creator 返回的值是这个 action 类型的对象。然后通过 store.dispatch() 进行分发。同步情况下一切都很完美,但是 reducer 无法处理异步的情况。那么我们就需要在 action 和 reducer 中间架起一座桥梁来处理异步。这就是 middleware。

中间件的由来与原理、机制:

export default function thunkMiddleware({ dispatch, getstate }) { return next => action => typeof action === 'function'? action (dispatch, getstate): next (action); } 

这段代码的意思是,中间件这个桥梁接受到的参数 action,如果不是 function 则和过去一样直接执行 next 方法(下一步处理),相当于中间件没有做任何事。如果 action 是 function,则先执行 action,action 的处理结束之后,再在 action 的内部调用 dispatch。

8.1 redux-thunk

到目前为止,我们学习到所有逻辑都是同步的。我们需要一个地方在我们的 Redux 应用程序中放置异步逻辑。这就需要使用中间件 redux-thunk。

thunk

是一种特定类型的 Redux 函数,可以包含异步逻辑。Thunk 是使用两个函数编写的:

  • 一个内部 thunk 函数,它以 dispatchgetState 作为参数;
  • 外部创建者函数,它创建并返回 thunk 函数;

counterSlice 导出的函数就是一个 thunk action creator 的例子。

// 下面这个函数就是一个 thunk ,它使我们可以执行异步逻辑 // 你可以 dispatched 异步 action `dispatch(incrementAsync(10))` 就像一个常规的 action // 调用 thunk 时接受 `dispatch` 函数作为第一个参数 // 当异步代码执行完毕时,可以 dispatched actions export const incrementAsync = amount => dispatch => { setTimeout(() => { dispatch(incrementByAmount(amount)) }, 1000) } 

我们可以像使用普通 Redux action creator 一样使用它们:

store.dispatch(incrementAsync(5)) 

8.2 redux-promise

Redux 异步逻辑另外一种解决方案 redux-promise 中间件。用 promise 对象代替 Redux-thunk 中的函数。

8.3 redux-saga

redux-saga 相比 redux-thunk、redux-promise 能够非侵入式结合 redux 进行开发。让你的 action 还是之前那个普通的 action 对象,然后你需要引入我 saga 中的一些任务、effect 作用等等来处理。

8.3.1 生成器函数 Generator

Generator 生成器函数,ES6 中提供异步编程的一种解决方案。有时候也被被人称为状态机,可以让函数中断执行,等你需要推一步就走一步,可以生成输出多个状态,所以又叫状态机。

生成器函数特征:

  1. 函数名前面增加 * 号。
  2. 必须使用关键字 yield(产出状态值)。
  3. yield 后面跟的是状态机生成的状态。即当遇到 yield 表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的 value 属性值。yield 表达式本身没有返回值,或者说总是返回undefined。
  4. next 方法可以带一个参数,该参数就会被当作上一个 yield 表达式的返回值。
function *test() { console.log("111111") yield; console.log("222222") yield; console.log("333333") yield; } let generator = test() // next() 执行器函数执行一次,直到遇到 yield 关键字 generator.next() // 111111 generator.next() // 222222 generator.next() // 333333 generator.next() // 没有任何输出了,已经结束了 function *test1() { console.log("111111") let value1 = yield "yield return 1 step"; console.log("222222", value1) let value2 = yield "yield return 2 step"; console.log("333333", value2) let value3 = yield "yield return 3 step"; console.log("333333", value3) } let generator1 = test1() let gen1 = generator1.next("1 next 参数") console.log(gen1) // {value: 'yield return 1 step', done: false} let gen2 = generator1.next("2 next 参数") console.log(gen2) // {value: 'yield return 2 step', done: false} let gen3 =generator1.next("3 next 参数") console.log(gen3) // {value: 'yield return 3 step', done: false} let gen4 =generator1.next("4 next 参数") console.log(gen4) // {value: undefined, done: true} 

异步链式调用更简单的写法 async-await 写法,async-await 本质是生成器的一套语法糖,内置了执行器函数。让异步变写得和同步的一样简单。但是这里 redux-saga 是基于生成器函数来实现的,我们了解即可:

async function test() { var res1 = await fetch(); var res2 = await fetch(res1); var res3 = await fetch(res2); } 
8.3.2 redux-saga 应用

在 saga 中,全局监听器和接收器使用 Generator 函数和 saga 自身的一些辅助函数实现对整个流程的管控。

7. redux-saga.png

// Component 组件内部 dispatch({action:"get-list"}) // WatcherSaga // saga.js 文件 function *watchSaga() { while(true) { // take 监听 组件发来的 action yield take("get-list") // fork 同步非阻塞执行函数 getList yield fork(getList) } } function *getList() { // 异步处理:call 函数发布异步请求 - 阻塞式调用 let res = yield call(getListAction) //这里传入返回值是promise对象的函数 // put 函数发出新的 action yield put({ type: "change-list", payload: res }) } function getListAction() { return new Promise((resolve, reject)=>{ setTime(()={ resolve(["111","222","333"]) },2000) }) } export default watchSage 
// store.js 文件 import {createStore, applyMiddleware} from 'redux' import reducer from ' /reducer' import createSagaMidlleWare from 'redux-saga' import watchSaga from •/ saga' const SagaMidlleWare = createSagaMidlleWare() const store = createStore(reducer, applyMiddleware(SagaMidlleWare)) SagaMidlleWare.run(latchSaga) //saga 任务, export default store 

多任务同时监听 all:

// WatcherSaga // saga2.js 文件 export default watchSage // 聚合统一监听多个任务 saga.js 文件 import {all} from 'redux-saga/effects' import watchSagal from '•/saga/sagal' import watchSaga2 from './saga/saga2' function *watchSaga(){ yield all([watchSaga1(),watchSaga2()]) } export default watchSaga 

多异步链式流程调用:

function *getList() { // 异步处理:call 函数发布异步请求 - 阻塞式调用 let res = yield call(getListAction) //这里传入返回值是promise对象的函数 let res1 = yield call(getListAction1, res) // put 函数发出新的 action yield put({ type: "change-list", payload: res1 }) } function getListAction() { return new Promise((resolve, reject)=>{ setTime(()={ resolve(["111","222","333"]) },2000) }) } function getListAction1(data) { return new Promise((resolve, reject)=>{ setTime(()={ resolve([...data, "444"]) },2000) }) } export default watchSage 

watchSaga 函数新写法-合并 take 和 fork:

// WatcherSaga // saga.js 文件 function *watchSaga() { /*while(true) { // take 监听 组件发来的 action yield take("get-list") // fork 同步非阻塞执行函数 getList yield fork(getList) }*/ yield takeEvery("get-list", getList) } 
8.3.3 redux-saga 应用场景

在 React-Redux 应用中,‌

redux-saga 主要用于管理复杂异步逻辑和副作用

‌,尤其在以下场景中具有显著优势:

多步骤异步操作:‌

当操作涉及多个顺序/并行的异步任务(如:登录 → 获取用户信息 → 加载权限列表),sagaGenerator 函数可用 yield 精确控制每一步流程,避免回调地狱‌

function* loginFlow() { yield call(loginAPI); // 步骤1:登录 yield call(fetchUserInfo); // 步骤2:获取用户信息 yield call(loadPermissions); // 步骤3:加载权限 } 

依赖异步结果的后续操作:

若后续操作需依赖多个异步任务结果(如:支付需同时验证账户余额和风控状态),saga 可通过 all 实现并行请求,并统一处理结果‌。

优选 saga 的场景:‌

多步骤异步

‌、‌

高可测性要求

‌、‌

长时运行任务

‌(如实时通信)‌。

简单场景(单一请求)可使用 redux-thunk 或 Redux Toolkit 内置方案‌。

9. Redux 插件

9.1 redux-persist

redux-persist 是一个用于 Redux 状态管理的持久化插件,允许将应用状态保存到本地存储(如 localStorage),以便在应用重启或页面刷新时恢复状态。必须配合 React-redux 使用。

评论

昵称
邮箱
主页