一文让你彻底弄懂Redux的基本原理以及其如何在React中使用!

8,262 阅读10分钟

作者自述(觉得没意思可以把这段跳过): 写这篇文章的初衷就是像标题所说,希望初学React-Redux的小伙伴们可以少走一些坑,我刚开始学的时候认为Redux不是很好理解(当时已经对Vuex和Pinia比较了解),感觉用起来很麻烦,不过在整理笔记的时候将这些状态管理的工具对比起来觉得有些地方真的很有趣,毕竟每个设计有每个设计的理念嘛😼 如果你是小白请紧跟文章的思路走,如果你是大佬,那么我认为这篇文章对于你复习React-Redux也是会有一点帮助滴。 好啦,话不多说,我们开始咯!

什么是Redux?它有什么用

Redux 是一个用于 JavaScript 应用的状态管理库,通常与 React 一起使用。它帮助开发者管理应用中各个组件之间的状态,使得状态的变化变得更加可预测和易于调试。

注意:Redux也可以不和React组合使用的哦(通常一起使用)

Redux基本原理

所有的状态都以对象树的方式 (state) 存放于单个 store 中。

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

新的 state 被创建后,对象会自动传递给所有注册了监听器的组件,从而触发组件的重新渲染,使得界面始终保持与当前的 state 对象一致。 Redux原理图

动态图理解(掘金大佬的图解Redux【之前存下来有助于理解,如果有侵权的话麻烦您联系我,我会跟您道歉并且立马删除!】): 掘金大佬的图解Redux

Redux在React中具体使用的方法

在React中使用redux,官方建议安装两个其他插件 - Redux Toolkit 和 React-Redux

  1. Redux Toolkit(RTK):官方推荐编写Redux逻辑的方式,是一套工具的集合集,简化书写方式

  2. React-Redux :用来 链接 Redux 和 React 组件的中间件 React-Redux原理

  3. 安装方式

    npm install @reduxjs/toolkit react-redux
    

下面我将详细讲解这两个插件的一些具体使用场景和常用函数。

Redux Toolkit(RTK)

非常草率的讲,就是简化Redux的使用方式推出的Toolkit(工具),这个工具中集合了方便使用Redux的一些函数。接下来我们来看一下这些函数是什么?怎么用?有什么用?

createSlice 函数

createSlice 函数的作用是创建一个 Redux 的 slice。它接受一个包含 reducer 函数、slice 名称和初始状态的配置对象,并返回一个包含 reducer 和 action creators 的对象。

参数

  • name:slice 的名称,用于标识状态的一部分。
  • initialState:slice 的初始状态,定义了状态的初始值。
  • reducers:一个对象,包含一组同步的 reducer 函数,用于更新状态。

返回值

createSlice 返回一个包含以下属性的对象:

  • name:slice 的名称。
  • reducer:一个 reducer 函数,用于处理来自 action creators 的动作并更新状态。
  • actions:一组 action creators,用于创建派发给 reducer 的动作。

示例

import { createSlice } from '@reduxjs/toolkit';

// 定义初始状态
const initialState = {
  count: 0,
};

// 创建一个 Redux slice
const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    // 定义同步的 reducer 函数
    increment(state) {
      state.count += 1;
    },
    decrement(state) {
      state.count -= 1;
    },
    // 可以接受额外参数的 reducer 函数
    incrementByAmount(state, action) {
      state.count += action.payload;
    },
  },
});

// 导出action creators
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// 导出reducer
export default counterSlice.reducer;

在上面的示例中,我们使用 createSlice 函数创建了一个名为 counter 的 slice。它包含一个名为 count 的状态和三个同步的 reducer 函数:increment、decrement 和 incrementByAmount。

通过increment, decrement, incrementByAmount派发动作

通过counterSlice.reducer处理动作

configureStore 函数

configureStore 函数的作用是创建一个 Redux store。它接受一个包含 reducer 函数和其他配置选项的对象,并返回一个 Redux store 实例。

参数

  • reducer:一个或多个 reducer 函数,用于处理来自 action creators 的动作并更新状态。
  • 其他配置选项:包括 middleware、devTools 等,用于配置 store 的行为。

返回值

configureStore 返回一个 Redux store 实例,它包含以下属性和方法:

  • getState():用于获取当前的状态。
  • dispatch(action):用于派发一个动作,以触发状态的更新。
  • subscribe(listener):用于添加一个状态变化的监听器,当状态发生变化时会被调用。
  • replaceReducer(nextReducer):用于替换当前的 reducer。

示例

import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './reducers'; // 导入根 reducer

// 创建 Redux store
const store = configureStore({
  reducer: rootReducer,
  // middleware: getDefaultMiddleware => getDefaultMiddleware(), // 使用默认的中间件
  // devTools: process.env.NODE_ENV !== 'production', // 在开发环境启用 Redux DevTools
});

export default store

在上面的示例中,我们使用 configureStore 函数创建了一个 Redux store。

我们传入了一个根 reducer rootReducer,它是一个包含所有 reducer 的对象。我们还配置了默认的中间件,并在开发环境下启用了 Redux DevTools。

React-Redux

想掌握 React-Redux 你首先要把它的概念弄清楚,这很重要!

通俗来讲,React-Redux将所有组件分成两大类:UI 组件和容器组件。

  1. UI组件:负责呈现页面。(React)
  2. 容器组件:负责管理数据和业务逻辑。(Redux)

有人可能问,Redux和React-Redux的区别是什么? 答案:React-Redux 的主要作用是简化在 React 应用中使用 Redux 的过程,提供方便的方法来连接 Redux store 和 React 组件。

下面我们来说一下React-Redux中常用的组件及方法。

Provider组件

Provider是React-Redux中的一个高阶组件,它的作用是将 Redux 的 store 传递给整个 React 应用程序,使得所有的组件都能够访问到 Redux 的状态。通过Provider,我们可以在 React 应用的任何地方使用 Redux 的状态和派发动作。

使用 Provider 的主要好处是,在整个应用程序中,任何一个组件都可以通过 connect 函数或者 useSelector 钩子来访问 Redux store 中的状态,而不需要手动地将 store 传递给每一个组件。这样做的好处有:

  1. 简化代码: 不需要在每一个组件中手动传递 store,通过 Provider,store 可以在整个应用程序中自动地传递给需要的组件。
  2. 避免 prop drilling: 避免了在组件层级结构中进行多层次的 prop 传递,提高了代码的可维护性和可读性。
  3. 一致性: 所有的组件都使用相同的 Redux store,保证了应用程序中状态的一致性。

示例

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

import store from './store';
import { Provider } from 'react-redux';

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
    <Provider store={store}>
      <App />
    </Provider>
)

在上面的示例中,我们将 Redux store 通过 Provider 传递给了根组件 <App />,这样在整个应用程序中的任何地方,我们都可以使用 React-Redux 提供的 hooks 或者高阶组件来访问 Redux 的状态,以及派发 Redux 的动作。

React组件使用store中的数据

useSelector钩子函数

useSelector 是 React-Redux 提供的一个钩子函数,它用于从 Redux store 中选择部分状态。通过 useSelector,我们可以在函数组件中订阅 Redux store 中的状态,并在状态变化时重新渲染组件。

它的作用类似于 connect 方法中的 mapStateToProps,但是更加简洁和直接。(后面说)


它接受一个选择器函数作为参数,并返回选择器函数计算得到的值。当 Redux store 中的状态发生变化时,组件将会重新渲染,以显示最新的状态。

示例
import { useSelector } from "react-redux"

function App() {
  const {count} = useSelector((state) => state.counter)
  return <div>{count}</div>
}

export default App

在上面的示例中,我们通过 useSelector 钩子函数选择了 Redux store 中的 counter 状态,并将其赋值给 counter 变量。随后,我们将这个状态显示在组件中,当 Redux store 中的 counter 状态发生变化时,组件将会重新渲染以显示最新的状态。

connect组件

connect 是 React-Redux 提供的一个函数,用于连接 React 组件和 Redux store。

上面已经说过 React-Redux 的概念了,按照我的理解,connect就是 React 和 Redux 中间的-

mapStateToProps

将Redux store中的状态映射到组件的props:

const mapStateToProps = state => {
  return {
    count: state.count,
  }
}
mapDispatchToProps

将 action creators 映射到组件的 props 中的回调函数:

const mapDispatchToProps = {
  increment,
  decrement,
}
示例
import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from './actions';

function App({ count, increment, decrement }) {
  return (
    <div>
      <button onClick={increment}> + </button>
      <p>{count}</p>
      <button onClick={decrement}> - </button>
    </div>
  )
}

const mapStateToProps = state => {
  return {
    count: state.count,
  }
}

const mapDispatchToProps = {
  increment,
  decrement,
}

// 使用 connect 函数连接组件和 Redux store
export default connect(mapStateToProps, mapDispatchToProps)(App)

在上面的示例中,我们通过 connect 函数将 Redux store 和组件连接起来。我们使用 mapStateToProps 函数将 Redux store 中的 count 状态映射到组件的 count props 中,同时使用 mapDispatchToProps 对象将 increment 和 decrement action creators 映射到组件的 increment 和 decrement props 中的回调函数。

export default connect(mapStateToProps, mapDispatchToProps)(App)

可能这里会有小伙伴很迷,这里其实是将 mapStateToProps 和 mapDispatchToProps 中定义的逻辑分别应用到 App 组件上,并将最终生成的容器组件导出。

React组件修改store中的数据

useDispatch钩子函数

useDispatch 是 React-Redux 提供的一个钩子函数,用于在函数组件中获取 Redux store 的 dispatch 函数。通过 useDispatch,我们可以在函数组件中派发 Redux actions,从而改变 Redux store 中的状态。

示例
import { useDispatch, useSelector } from "react-redux"
// 导入actionCreater
import { inscrement, decrement } from "./store/modules/counterSlice"

function App() {
  const { count } = useSelector((state) => state.counter)
  // 使用useDispatch()函数
  const dispatch = useDispatch()
  return (
    <div>
      {/* 加 */}
      <button onClick={() => dispatch(inscrement())}> + </button>
      {count}
      {/* 减 */}
      <button onClick={() => dispatch(decrement())}> - </button>
    </div>
  )
}

export default App

在上面的示例中,我们通过 useDispatch 钩子函数获取了 Redux store 的 dispatch 函数,并将其赋值给 dispatch 变量。随后,我们可以在组件中使用 dispatch 函数来派发 Redux actions,例如在按钮的点击事件处理函数中派发 increment 和 decrement actions。

Redux异步action处理

虽然 Redux 主要用于处理同步的状态更新,但有时我们需要处理异步的操作,例如从服务器获取数据或执行一些异步任务。在 Redux 中处理异步操作通常需要使用中间件来实现。

相信能看到这的朋友们不至于我长篇大论再给您说异步吧...

Redux Thunk 中间件

将Redux中的异步action处理时,我们不得不提到Redux Thunk。

它是 Redux 提供的一个中间件,它允许我们 dispatch 函数而不仅仅是普通的 action 对象。这样,我们就可以在 action creators 中返回一个函数,而不是一个普通的 action 对象。这个函数可以接受dispatch和getState作为参数,并在其中执行异步操作。

示例

*这里就不贴代码段了,来张图看的更清晰一点 在这里插入图片描述

在上面的示例中第二点我要做详细的解释(作者刚学的时候绕的非常不舒服)

错误的想法:直接调用fetchChannels()

正确的想法:dispatch(fetchChannels())

原因:当你调用 dispatch(fetchChannels()) 时,Redux Thunk 中间件会拦截这个 action,并判断它是否是一个函数。如果是函数,则 Redux Thunk 会执行这个函数,并将 dispatch 方法作为参数传递给它。在这个函数中,你可以进行异步操作,比如发送网络请求。当异步操作完成后,你可以再次使用 dispatch 来派发普通的 action 对象,这样 Redux store 就能够正确地更新状态。

好啦, 以上就是我分享的Redux的相关的常用知识啦! 很后悔没有早点学习React,因为真的超级爽!(当然现在努力也不算晚哈哈哈)如果你已经看到这里,请收下我的感谢,如果文章哪里有错误或者不太妥当的地方请评论留言,我会积极听取大家的意见的!也欢迎在评论区里讨论🥰