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
相关推荐
好_快24 分钟前
Lodash源码阅读-Hash
前端·javascript·源码阅读
IT、木易30 分钟前
如何在 HTML 中创建一个有序列表和无序列表,它们的语义有何不同?
前端·html
好_快32 分钟前
Lodash源码阅读-isKeyable
前端·javascript·源码阅读
一蓑烟雨,一任平生38 分钟前
牛客网编程题调试问题(华为研发工程师编程题&JavaScript Node调试)
前端·javascript·算法
江沉晚呤时2 小时前
深入解析 .NET 中的依赖项加载机制:原理、实现与最佳实践
前端·数据库·c#·.netcore
哟哟耶耶2 小时前
knowledge-微前端(多个前端应用聚合的一个应用架构体系,每个小的应用可独立运行,独立开发,独立部署上线)
前端
Enjoy_zhuo2 小时前
xss-labs第八、九关卡以及XSS GAME的Ok,Boomer关卡
前端·安全·xss
hikktn3 小时前
【开源宝藏】30天学会CSS - DAY2 第二课 Loader Ring Demo
前端·css·开源
Q_0046 小时前
umi自带的tailwindcss修改为手动安装
react.js·postcss
晓夜残歌6 小时前
安全基线-rm命令防护
运维·服务器·前端·chrome·安全·ubuntu