RTK的基本使用

Redux是一个帮助我们管理state的容器:Redux是javascript的状态容器,提供了可预测的状态管理

在vue2中使用的是vuex,vue3中使用的是pinia

而在react中使用的是redux进行状态管理或是RTK(redux-toolkit),在现在当然也有一些其他的状态管理 zustand,easypeasy或是使用vite来创建react的项目

但是一些老的react16、17的项目或是之前的都普遍用的是redux或是RTK来进行状态管理

redux可以完全脱离react在其他的项目中运行,比如uniapp等,并且包也是非常的小,加上依赖一共才2kb

Redux 三大原则

  1. 单一数据源:整个应用状态存储在单个 store 中
  2. 状态只读:只能通过 action 修改状态
  3. 纯函数修改:使用纯函数 reducer 执行状态更新
tsx 复制代码
// 伪代码结构
{
  store: {
    state,        // 存储应用状态
    dispatch,     // 触发 action 的方法
    subscribe,    // 监听状态变化
    getState     // 获取当前状态
  },
  action: { type, payload }, // 描述状态变化的对象
  reducer: (state, action) => newState // 状态处理函数
}

来个计数的小例子

创建action

jsx 复制代码
// actionCreator.jsx
import { INCREMENT, DECREMENT } from './constants.jsx';


export const increment = (num) => {
  return {
    type: INCREMENT,
    payload: num
  };
};


export const decrement = (num) => {
  return {
    type: DECREMENT,
    payload: num
  };
};

常量参数,类型

jsx 复制代码
// constants.jsx
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

store实例

jsx 复制代码
import { createStore } from 'redux';
import reducer from './reducer'

const store = createStore(reducer)

export default store;

reducer函数

jsx 复制代码
import { INCREMENT, DECREMENT } from './constants.jsx';

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

// 定义 reducer 函数
const counterReducer = (state = initialState, action) => {
  switch (action.type) {
    case INCREMENT:
      return {
        ...state,
        count: state.count + action.payload
      };
    case DECREMENT:
      return {
        ...state,
        count: state.count  +action.payload
      };
    default:
      return state;
  }
};

export default counterReducer;
jsx 复制代码
// app.jsx
import store from './store';
import { increment, decrement } from './store/actionCreator';

const App = () => {
  
  const changeNum = (num) => {
    return () => {
      if (num > 0) {
        store.dispatch(increment(num));
        console.log(store.getState().count);
        
      } else {
        store.dispatch(decrement(num));
        console.log(store.getState().count);
      }
    };
  };

  return (
    <>
      <button onClick={changeNum(1)}>+1</button>
      <button onClick={changeNum(5)}>+5</button>
      <button onClick={changeNum(-4)}>-4</button>
      <button onClick={changeNum(-10)}>-10</button>
    </>
  );
};

export default App;

最终效果

RTK基本使用

安装

bash 复制代码
pnpm i @reduxjs/toolkit react-redux

核心api

configureStore: 包装createStore以提供简化的配置选项和良好的默认值,它可以自动组合slice reducer,添加你提供的任何 Redux 中间件,redux-thunk 默认包含 ,并启用 Redux DevTools Extension

createSlice:接受 reducer 函数的对象,切片名称和初始状态,并自动切片 reducer ,并带有相应的 actions

createAsycThunk:接受动作类型字符串和一个返回承诺的函数,并生成一个pending/fulfilled/reject二点基于改承诺分派动作类型的 thunk

项目结构

bash 复制代码
src/
├── index.js          # 应用入口
├── App.js
├── views/
│   ├── Home.js
│   └── Profile.js
└── store/
    ├── index.js      # store 配置
    └── modules/
        └── counter.js # counter slice

入口文件 main.jsx

jsx 复制代码
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import { Provider } from 'react-redux'  // redux 连接组件

import store from './store/index.js' // 导入创建好的 store

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <Provider store={store}> // 必须用 Provider 包裹整个应用提供 store 
      <App />
    </Provider>
  </StrictMode>
)

根组件 App.jsx

jsx 复制代码
import React from 'react'

import Home from './views/home'
import Profile from './views/profile'
import './styles/index.css'

const App = () => {
  return (
    <div className='pages'>
      <Home></Home>
      <Profile></Profile>
    </div>
  )
}

export default App

Home 组件

jsx 复制代码
import { useSelector, useDispatch } from 'react-redux'
import { increment, decrement } from '../store/modules/counter'

const Home = () => {
  // 从 store 获取数据
  const count = useSelector(state => state.counter.count)
  
  // 获取 dispatch 方法
  const dispatch = useDispatch()

  return (
    <div className="home">
      <h1>Home Page Counter: {count}</h1>
      <div className="buttons">
        <button onClick={() => dispatch(increment(1))}>+1</button>
        <button onClick={() => dispatch(decrement(1))}>-1</button>
      </div>
    </div>
  )
}

export default Home

关键参数说明:

  • useSelector:从 store 提取数据的 hook
  • state.counter.count:对应 store 的模块结构
  • useDispatch:获取 dispatch 方法用于触发 action
  • increment(1):调用 action creator 并传递 payload

Profile组件

jsx 复制代码
import { useSelector } from 'react-redux'

const Profile = () => {
  // 共享同一个 counter 状态
  const count = useSelector(state => state.counter.count)

  return (
    <div className="profile">
      <h1>Profile Page Counter: {count}</h1>
      <p>This component demonstrates state sharing across components</p>
    </div>
  )
}

export default Profile

注意 使用数据时 访问的是 state.counter.count 而不是 state.count

因为我们在配置 store 的时候合并了 reducer

jsx 复制代码
const store = configureStore({
  reducer: {
    counter: counterReducer // 模块化 reducer
  },
})

后续如果需要添加模块直接在 modules 文件夹下进行新建

store 配置

jsx 复制代码
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './modules/counter' // 导入 counter slice

const store = configureStore({
  reducer: {
    counter: counterReducer // 模块化 reducer
  },
  // 默认包含以下中间件:
  // 1. redux-thunk
  // 2. 开发环境下的 Redux DevTools 集成
})

export default store

配置参数说明:

  • configureStore 自动配置:
    • 合并多个 reducer
    • 集成 Redux Thunk 中间件
    • 开发环境自动启用 Redux DevTools
  • 模块化结构便于扩展

counter 模块

jsx 复制代码
import { createSlice } from '@reduxjs/toolkit'

const counterSlice = createSlice({
  name: 'counter', // 命名空间(自动生成 action type 前缀)
  initialState: {
    count: 0       // 初始状态
  },
  reducers: {
    // 自动生成 action creators
    increment: (state, action) => {
      // 使用 Immer 库,可以直接修改 state
      state.count += action.payload || 1
    },
    decrement: (state, action) => {
      state.count -= action.payload || 1
    },
    // 可以添加更多 reducer...
  }
})

// 导出生成的 action creators
export const { increment, decrement } = counterSlice.actions

// 导出 reducer 供 store 配置使用
export default counterSlice.reducer

配置参数说明

name:切片名称,用于生成 action type 前缀(如 "counter/increment")

initialState:模块的初始状态

reducers:定义 reducer 函数和对应的 action creators

最终实现效果

异步操作

Redux Toolkit 官方推荐使用 createAsyncThunk + extraReducers 处理异步流程

yaml 复制代码
store/
├── modules/
│   ├── pic.js      # 新增图片模块
│   └── counter.js   # 之前的计数器模块

createAsyncThunk 用于创建一个异步的action

extraReducers 用于处理 异步 action 在执行请求的时候对应状态的处理

js 复制代码
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import { getPic } from '@/apis/pic'

// 创建异步 Thunk(核心异步操作)是一个异步的 action

/* 当 createAsyncThunk 创建出来的 action 被 dispatch 时,会存在三种状态
pending:正在请求数据中结果还未返回
fulfilled: 请求成功
rejected: 请求失败

对应是否请求结果的状态需要在 extraReducers 这个额外的 reducer 里面监听结果 
*/
export const fetchPic = createAsyncThunk(
  'pic/fetchPic',

  /* 
  第一个参数:调用 fetchPic 时传入的参数(如 fetchPic({ id: 1 }))。

  第二个参数:thunkAPI 对象,包含 dispatch、getState、rejectWithValue 等工具。
  
  当请求出现异常情况或是错误时,返回一个自定义错误对象进行精细化管理
  */
  async (params, { rejectWithValue }) => {
    const response = await getPic()

    if (response.success === true) {
      return response.data
    }

    return rejectWithValue({
      message: response.message || '图片获取失败',
      code: response.code,
    })
  }
)

const initialState = {
  data: null,
  status: 'idle', // 异步状态标识('idle' | 'loading' | 'succeeded' | 'failed')
  error: null,
}

// 创建 Slice
const picSlice = createSlice({
  name: 'pic', // Slice 名称(用于生成 action types)
  initialState,

  // 同步 Reducers(可选)
  reducers: {
    resetPic: state => {
      state.data = null
      state.status = 'idle'
      state.error = null
    },
  },

  // 异步 Reducers
  /* 
  builder 是一个对象,它包含了三个方法:addCase、addMatcher 和 addDefaultCase。
  addCase 方法用于添加一个异步 action 的处理函数。
  addMatcher 方法用于添加一个异步 action 的匹配函数。
  addDefaultCase 方法用于添加一个默认的处理函数。
  */

  /* 
  addCase 方法的第一个参数是一个异步 action,第二个参数是一个处理函数。回调里面的;两个参数: state 和 action。
  state 是当前的状态,action 是异步 action 的结果。
  */
  extraReducers: builder => {
    builder

      .addCase(fetchPic.pending, state => {
        state.status = 'loading'
        state.error = null
      })

      .addCase(fetchPic.fulfilled, (state, action) => {
        state.status = 'succeeded'
        state.data = action.payload
      })

      .addCase(fetchPic.rejected, (state, action) => {
        state.status = 'failed'
        state.error = action.payload
      })
  },
})

export const { resetPic } = picSlice.actions

export default picSlice.reducer
相关推荐
小小小小宇11 分钟前
LLM 长期记忆构建
前端
lichenyang45323 分钟前
从 Express 老项目到 NestJS + Docker:一次车辆管理系统的渐进式重构
前端
Momo__2 小时前
VueUse createReusableTemplate —— 单文件组件内的模板复用神器
前端·vue.js
无名氏同学2 小时前
React 16-19 新特性
react.js
程序员小富2 小时前
我开源了一个开发者专属的智能 JSON 工具,得到了媳妇高度认可
前端·vue.js·后端
小小小小宇2 小时前
程序员如何给 LLM 装工具以及看懂推理过程
前端
写代码的皮筏艇2 小时前
React中的forwardRef
前端·react.js·面试
槑有老呆2 小时前
花三个月工资请了个 AI 程序员,结果它连青岛啤酒股价都查不了
前端
风骏时光牛马2 小时前
Verilog开发常见问题汇总解析
前端
子兮曰2 小时前
AI Coding Method Map:一张图看懂 AI 编程的完整链路
前端·人工智能·后端