React全家桶 - 【Redux】 - 环境准备、使用步骤、提交action传参、异步状态操作

一、环境准备

1.1 安装插件

  • 在 React 中使用 Redux,官方要求安装两个插件 Redux Toolkitreact-redux
  • Redux Toolkit :(RTK)
    • 官方推荐编写 Redux 逻辑的方式,是一套工具的集合,简化书写方式
      • 简化 store配置方式
      • 内置 immer 支持 可变式状态修改
      • 内置 thunk 更好的 异步创建
    • 安装
      • npm install @reduxjs/toolkit
  • react-redux
    • 用来链接 ReduxReact组件 的中间件;
    • 安装
      • npm install react-redux
  • 统一安装
    • npm i @reduxjs/toolkit react-redux

安装完成之后,启动项目的时候,可能会报错,报错信息如下:

swift 复制代码
[eslint] Plugin "react" was conflicted between "package.json >> eslint-config-react-app >> 
D:\Table top\training-project\React\training-project\react-redux-pro\node_modules\eslint-
config-react-app\base.js" and "BaseConfig >> D:\Table top\training-project\React\training-
project\react-redux-pro\node_modules\.pnpm\eslint-config-react-app@7.0.1_@babel+plugin-
syntax-flow@7.23.3_@babel+plugin-transform-react-
_ymo2r6j4jsijv4xl6xbyks3h6q\node_modules\eslint-config-react-app\base.js".

这时我们找到项目里的 package.json 文件,摁下 Ctrl + S 保存,就好了(我的就是这么解决了,如果这样不行,大家可以自行百度一下哈);

1.2 store目录结构设计

  • 通常集中状态管理的部分都会单独创建一个 store 目录;
  • 应用通常会有很多个子 store 模块,所以创建一个 modules 目录,在内部编写业务分类的子 store
  • store 中的入口文件 index.js 的作用是组合 modules 中所有的子模块,并导出 store

二、Redux 与 React - 基本使用步骤

  • 基于上篇文章,实现点击按钮,数量count变化;
  • counterStore模块:里面写主要的逻辑,在当前案例,主要写的是增和减;
  • 整体流程如下图所示:(同步修改

2.1 使用 React Toolkit 创建 counterStore

  • 目标文件:

    • src/store/modules/counterStore.js;
    • 子store:
    js 复制代码
    // 导入创建 store 的方法
    import { createSlice } from '@reduxjs/toolkit';
    
    const counterStore = createSlice({
      name: 'counter',
    
      // 初始状态数据
      initialState: {
        count: 0
      },
    
      // 修改数据 - 同步方法
      // 支持直接修改
      reducers: {
        // 增方法
        increment: (state) => {
          state.count++;
        },
    
        // 减方法
        decrement: (state) => {
          state.count--;
        }
      }
    });
    
    // 解构出创建 action 对象的函数(actionCreater)
    const { increment, decrement } = counterStore.actions;
    
    // 获取 reducer 函数
    const counterReducer = counterStore.reducer;
    
    // 以 按需导出 的方式 导出 actionCreater
    export { increment, decrement };
    
    // 以默认导出的方式导出 reducer函数
    export default counterReducer;
  • 目标文件:
    *

    • src/store/index.js;
    • 根store:
    js 复制代码
    // 导入组合函数
    import { configureStore } from '@reduxjs/toolkit';
    // 导入 子store 模块
    import counterReducer from './modules/counterStore';
    
    // 该方法会生成一个 根store
    const store = configureStore({
      reducer: {
        // counter ===> 子store模块名:后面在组件中使用store数据的时候会用到
        counter: counterReducer
      }
    });
    
    export default store;
  • 优化

    • 在后续,我们需要使用对应store模块中的数据,我们都是从 ./store/modules/xxx.js 中进行按需导入;如果一个文件中,需要使用不同模块的数据,这样就显得很麻烦;

    • 优化处理

      • 我们可以将子模块中所有的按需导出,先导入到 ./store/index.js 中,然后从 index.js 中导出;
      js 复制代码
      // 只需在 inde下.js 中添加此代码即可
      export * from './modules/xxx';

2.2 为 React 注入 store

recat-redux 负责把 ReduxReact 链接起来,内置 Provider组件 (该插件的内置组件) 通过 store 参数 把创建好的 store实例 注入到应用中,链接正式建立;

  • 目标文件:

    • src/index.js
    js 复制代码
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import './index.css';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    
    // 导入 根store
    import store from './store';
    // 导入 react-redux 的内置组件 Provider
    import { Provider } from 'react-redux';
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <React.StrictMode>
        {/* 使用 Provider内置组件,将 App根组件 包裹起来,使用 store 参数,将 根store实例 传入组件 */}
        <Provider store={store}>
          <App />
        </Provider>
      </React.StrictMode>
    );
    
    reportWebVitals();

2.3 React组件 使用 store 中的数据

  • 在 React组件 中使用 store 中的数据,需要用到一个钩子函数 useSelector
  • useSelector 作用:
    • store中的数据 映射到 组件中;
    • 注意
      • useSelector 只能写在 Hook 内;
  • 使用样例如下:
  • 代码展示:

    • 目标文件:App.js
    js 复制代码
    import logo from './logo.svg';
    import './App.css';
    import { useSelector } from 'react-redux';
    
    function App() {
      // 使用 store 中的数据
      const { count } = useSelector((state) => state.counter);
    
      return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <p>{count}</p>
          </header>
        </div>
      );
    }
    
    export default App;

2.4 React组件 修改 store中的数据

  • redux 中,有且仅有 一种 修改store数据 的 方式,提交action
  • React组件修改 store中的数据 需要借助另外一个 hook函数 - useDispatch
  • useDispatch
    • 作用 :生成提交 action对象dispatch函数
  • 代码展示:

    • 目标文件:App.js
    jsx 复制代码
    import logo from './logo.svg';
    import './App.css';
    import { useSelector, useDispatch } from 'react-redux';
    // DONE 导入创建action对象的方法
    import { decrement, increment } from './store';
    
    function App() {
      // DONE 使用 store 中的数据
      const { count } = useSelector((state) => state.counter);
    
      // DONE 得到 dispatch 函数
      const dispatch = useDispatch();
    
      return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <hr />
            <div>
              {/* DONE 调用 dispatch 函数提交 action */}
              {/* dispatch函数需要的参数是 action 对象,action对象是通过 decrement / increment (actionCreate)方法得到的 */}
              {/* decrement / increment 方法一定要执行,只有执行了才能得到 action 对象 */}
              <button onClick={() => dispatch(decrement())}>-</button>
              <button onClick={() => dispatch(increment())}>+</button>
              <p>{count}</p>
            </div>
          </header>
        </div>
      );
    }
    
    export default App;

三、Redux 与 React - 提交action传参

  • 在上述案例中,我们虽然使用 dispatch 提交了 action对象,但是没有传参,现在就来看一下提交action的同时传递参数的情况;
  • 本节也是结合小案例去看;
  • 需求
    • 组件中有两个按钮,add to 10add to 20可以直接把 count 值修改到对应的数字,目标 count 值是在组件中传递过去的,需要在 提交action 的时候 传递参数;
  • 实现方法 🎯:
    • reducers同步修改方法 中添加 action对象参数 ,在 调用actionCreate 的时候 传递参数 ,参数会被传递到 action对象 payload属性 上
    • 注意
      • 如果没有传递参数,action.payload = undefined
      • 如果传递了参数,action.payload = 传递的参数
  • 代码展示:
    • 目标文件:src/store/modules/counterStore.js

      js 复制代码
      // 导入创建 store 的方法
      import { createSlice } from '@reduxjs/toolkit';
      
      const counterStore = createSlice({
        name: 'counter',
      
        // 初始状态数据
        initialState: {
          count: 0
        },
      
        // 修改数据 - 同步方法
        // 支持直接修改
        reducers: {
          /** 加法 */
          increment: (state, { payload }) => {
            state.count += payload || 1;
          },
      
          /** 减法 */
          decrement: (state) => {
            state.count--;
          },
          
          /** 增加指定数量 */
          addToNum(state, action) {
            state.count += action.payload;
          }
        }
      });
      
      // 解构出创建 action 对象的函数(actionCreater)
      const { increment, decrement, addToNum } = counterStore.actions;
      
      // 获取 reducer 函数
      const counterReducer = counterStore.reducer;
      
      // 以 按需导出 的方式 导出 actionCreater
      export { increment, decrement, addToNum };
      
      // 以默认导出的方式导出 reducer函数
      export default counterReducer;
    • 目标文件:src/App.js

      jsx 复制代码
      import logo from './logo.svg';
      import './App.css';
      import { useSelector, useDispatch } from 'react-redux';
      // DONE 导入创建action对象的方法
      import { decrement, increment, addToNum } from './store';
      
      function App() {
        // DONE 使用 store 中的数据
        const { count } = useSelector((state) => state.counter);
      
        // DONE 得到 dispatch 函数
        const dispatch = useDispatch();
      
        return (
          <div className="App">
            <header className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <hr />
              <div>
                {/* DONE 调用 dispatch 函数提交 action */}
                {/* dispatch函数需要的参数是 action 对象,action对象是通过 decrement / increment (actionCreate)方法得到的 */}
                <button onClick={() => dispatch(decrement())}>-</button>
                <button onClick={() => dispatch(increment())}>+</button>
                <p>{count}</p>
              </div>
              <hr />
              <div>
                <p>{count}</p>
                <button onClick={() => dispatch(increment(10))}>add to 10</button>
                <button onClick={() => dispatch(addToNum(20))}>add to 20</button>
              </div>
            </header>
          </div>
        );
      }
      
      export default App;

四、Redux 与 React - 异步状态操作

  • 之前做的都是同步操作,都是同步修改state,但是实际的开发中,我们还有异步的操作(等到接口获取数据之后进进行操作);
  • 还是用小案例的形式来说异步操作状态;
  • 需求
    • 请求接口,等接口返回数据之后,将数据使用redux保存起来;
    • 使用store中的数据,渲染列表;
  • 基本流程:
    • 与之前同步修改的流程基本一致,就增加了一个修改的配置;
  • 基本步骤
    • 创建 store 的写法保持不变,配置同步修改状态的方法;
    • 单独封装一个函数,在函数欸不 return 一个函数,在新函数中:
      • 封装异步请求,获取数据;
      • 调用同步 actionCreater 传入异步数据生成一个 action 对象,并使用 dispatch 提交;
    • 组件中 dispatch 的写法保持不变;
相关推荐
前端郭德纲2 小时前
浅谈React的虚拟DOM
前端·javascript·react.js
2401_879103683 小时前
24.11.10 css
前端·css
ComPDFKit4 小时前
使用 PDF API 合并 PDF 文件
前端·javascript·macos
yqcoder4 小时前
react 中 memo 模块作用
前端·javascript·react.js
优雅永不过时·4 小时前
Three.js 原生 实现 react-three-fiber drei 的 磨砂反射的效果
前端·javascript·react.js·webgl·threejs·three
神夜大侠7 小时前
VUE 实现公告无缝循环滚动
前端·javascript·vue.js
明辉光焱7 小时前
【Electron】Electron Forge如何支持Element plus?
前端·javascript·vue.js·electron·node.js
柯南二号7 小时前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
wyy72938 小时前
v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
前端·ui·html
前端郭德纲8 小时前
ES6的Iterator 和 for...of 循环
前端·ecmascript·es6