6.Redux状态管理

书诚小驿2025/07/01前端知识库React

一、Redux 状态管理

1、Redux 基础介绍

Reduxopen in new window

2、Redux 基础使用

  1. redux和redux-devtools-extension 安装

redux-devtools-extension扩展是redux的调试工具 且注意redux-devtools-extension现在的版本要求的 redux 版本是 ^3.1.0 || ^4.0.0

npm install redux@4.0.0
npm install --save redux-devtools-extension
  1. 创建src/store/index.js文件
import { createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";

function couterReducer(state = { count: 0 }, action) {
  switch (action.type) {
    case "inc":
      return { count: state.count + 1 };
    default:
      return state;
  }
}
// 创建store
const store = createStore(couterReducer, composeWithDevTools());
export default store;
  1. 创建src/views/About/About.jsx文件
import React from 'react'
import store from "../../store";
import { useState } from "react";
export default function About() {
  const [count, setCount] = useState()
  const handleClick = () => {
    store.dispatch({ type: "inc" })
  }
  // 订阅store,更新count
  store.subscribe(() => {
    setCount(store.getState().count)
  })
  return (
    <>
      {count || 0}
      <button onClick={handleClick}>+</button>
    </>
  )
}

3、Redux 进阶使用

react-redux简化redux的使用

  1. 安装react-redux
npm install react-redux
# 如果有依赖冲突,使用 --legacy-peer-deps 选项
npm install react-redux --legacy-peer-deps

这种方法会忽略对等依赖冲突,但可能会导致潜在的兼容性问题。

  1. <Provider store={store}>

src/index.js

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import reportWebVitals from "./reportWebVitals";
import { RouterProvider } from "react-router-dom";
import router from "./router/index";
import { Provider } from "react-redux";
import store from "./store";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <RouterProvider router={router}></RouterProvider>
    </Provider>
  </React.StrictMode>
);
  1. useSelector和useDispatch

src/views/About/About.jsx

import React from 'react'
import { useSelector, useDispatch } from "react-redux";
export default function About() {
  const count = useSelector((state) => state.count)
  const handleClick = () => {
    dispatch({
      type: 'inc',
      payload: 5,
    })
  }
  const dispatch = useDispatch()
  return (
    <>
      {count || 0}
      <button onClick={handleClick}>+</button>
    </>
  )
}

src/store/index.js

import { createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";

function couterReducer(state = { count: 0 }, action) {
  switch (action.type) {
    case "inc":
      return { count: state.count + action.payload };
    default:
      return state;
  }
}
// 创建store
const store = createStore(couterReducer, composeWithDevTools());
export default store;

二、如何处理多个reducer函数及redux模块化

1、redux combineReducers方法,合并多个reducer

  1. src/store/modules/counter.js
function counterReducer(state = { count: 0 }, action) {
  switch (action.type) {
    case "counter/inc":
      return { count: state.count + action.payload };
    default:
      return state;
  }
}
export default counterReducer;

  1. src/store/index.js
import { createStore, combineReducers } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";

import counterReducer from "./modules/counter";
// 创建store,combineReducers合并多个reducer
const store = createStore(
  combineReducers({
    counter: counterReducer
  }),
  composeWithDevTools()
);
export default store;
  1. src/views/About/About.jsx
import React from 'react'
import { useSelector, useDispatch } from "react-redux";
export default function About() {
  const count = useSelector((state) => state.counter.count)
  const handleClick = () => {
    dispatch({
      type: 'counter/inc',
      payload: 5,
    })
  }
  const dispatch = useDispatch()
  return (
    <>
      {count || 0}
      <button onClick={handleClick}>+</button>
    </>
  )
}

2、redux-thunk中间件处理异步action

dispatch默认只支持对象字面量,通过redux-thunk中间件可以处理异步action和回调函数,通过applyMiddleware方法让中间件生效

  1. 安装redux-thunk
npm install redux-thunk
# 如果有依赖冲突,使用 --legacy-peer-deps 选项
npm install redux-thunk --legacy-peer-deps
  1. src/store/index.js
import { createStore, combineReducers } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import { applyMiddleware } from "redux";
import counterReducer from "./modules/counter";
// 创建store,combineReducers合并多个reducer
const store = createStore(
  combineReducers({
    counter: counterReducer
  }),
  composeWithDevTools(applyMiddleware())
);
export default store;
  1. src/views/About/About.jsx
import React from 'react'
import { useSelector, useDispatch } from "react-redux";
export default function About() {
  const count = useSelector((state) => state.counter.count)
  const handleClick = () => {
    dispatch((dispatch) => {
      setTimeout(() => {
        dispatch({
          type: 'counter/inc',
          payload: 5,
        })
      }, 2000);
    })
  }
  const dispatch = useDispatch()
  return (
    <>
      {count || 0}
      <button onClick={handleClick}>+</button>
    </>
  )
}

三、redux-toolkit(RTK)改善redux使用体验

  • 可以自动跟redux devtools结合,不需要下载模块进行生效
  • 数据不需要再通过返回值进行修改,像vue一样可以直接修改
  • 内置了redux-thunk这个异步插件
  • 代码风格更好,采用选项式编写程序

1、Redux Toolkit 基础使用

  1. 安装redux-toolkit
npm i @reduxjs/toolkit
  1. src/store/modules/counter.js
import { createSlice } from "@reduxjs/toolkit";

const counterSlice = createSlice({
  // 触发dispatch的命名空间
  name: "counter",
  // initialState 初始化共享状态
  initialState: {
    value: 0,
  },
  // 编写reducer方法
  reducers: {
    increment: (state, action) => {
      state.value += action.payload;
    },
  },
});

export const { increment } = counterSlice.actions;

export default counterSlice.reducer;
  1. src/store/index.js
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./modules/counter";

const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

export default store;
  1. src/index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import reportWebVitals from "./reportWebVitals";
import { RouterProvider } from "react-router-dom";
import router from "./router/index";
import { Provider } from "react-redux";
import store from "./store";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <RouterProvider router={router}></RouterProvider>
    </Provider>
  </React.StrictMode>
);
reportWebVitals();
  1. src/views/About/About.jsx
import React from 'react'
import { useSelector, useDispatch } from "react-redux";
export default function About() {
  const dispatch = useDispatch()
  const value = useSelector((state) => state.counter.value)
  const handleClick = () => {
    dispatch({
      type: 'counter/increment',
      payload: 2
    })
  }
  return (
    <>
      {value || 0}
      <button onClick={handleClick}>+</button>
    </>
  )
}

2、Redux Toolkit 如何处理异步任务

  1. createAsyncThunk方法创建异步任务
  2. extraReducers配置额外的reducer

extraReducers 是 Redux Toolkit 提供的一个强大功能,用于在 createSlice 中定义额外的 reducer,以便处理在当前 slice 之外定义的 action 类型。它允许一个切片 reducer 响应和更新自己的状态,同时处理其他切片或模块生成的 action 类型

src/store/modules/counter.js

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";

const initialState = {
  value: 0,
};

export const counterTestAction = createAsyncThunk(
  "counter/testAction",
  async () => {
    const res = await new Promise((resolve) => {
      setTimeout(() => {
        resolve("success");
      }, 1000);
    });
    return res;
  }
);

const counterSlice = createSlice({
  name: "counter",
  initialState: { ...initialState },
  reducers: {
    increment: (state, action) => {
      state.value += action.payload;
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(counterTestAction.fulfilled, (state, action) => {
        console.log(
          "counterTestAction.fulfilled 成功触发",
          "方法一处理异步操作方式"
        );
      })
      .addCase(counterTestAction.rejected, (state, action) => {
        console.log("counterTestAction.rejected 失败触发");
      });
  },
});

export default counterSlice.reducer;

src/views/About/About.jsx

import React from 'react'
import { useSelector, useDispatch } from "react-redux";
import { counterTestAction } from "../../store/modules/counter";
export default function About() {
  const dispatch = useDispatch()
  const value = useSelector((state) => state.counter.value)
  const handleClick = () => {
    dispatch(counterTestAction()).then((res) => {
      console.log('方法二处理异步操作方式', res)
    })
  }
  return (
    <>
      {value || 0}
      <button onClick={handleClick}>+</button>
    </>
  )
}

四、通过redux-persist持久化存储

  1. 安装redux-persist
npm i redux-persist
# 如果有依赖冲突,使用 --legacy-peer-deps 选项
npm install redux-persist --legacy-peer-deps
import { createRoot } from 'react-dom/client'
import { configureStore } from '@reduxjs/toolkit'
import {
  persistStore,
  persistReducer,
  FLUSH,
  REHYDRATE,
  PAUSE,
  PERSIST,
  PURGE,
  REGISTER,
} from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import { PersistGate } from 'redux-persist/integration/react'

import App from './App'
import rootReducer from './reducers'

const persistConfig = {
  key: 'root',
  version: 1,
  storage,
}

const persistedReducer = persistReducer(persistConfig, rootReducer)

const store = configureStore({
  reducer: persistedReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
      },
    }),
})

let persistor = persistStore(store)

const container = document.getElementById('root')

if (container) {
  const root = createRoot(container)

  root.render(
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <App />
      </PersistGate>
    </Provider>,
  )
} else {
  throw new Error(
    "Root element with ID 'root' was not found in the document. Ensure there is a corresponding HTML element with the ID 'root' in your HTML file.",
  )
}
最后更新时间' 2025/7/6 18:57:44