react+redux+toolkit来实现公共数据的处理-对比vuex

发布于:2025-08-14 ⋅ 阅读:(17) ⋅ 点赞:(0)

前言:

        在react中处理公共数据有很多种方法,我们这里来说一说用 react+redux+toolkit 来实现公共数据的处理。

功能对比 redux与Vuex

功能 Vuex Redux Toolkit
状态定义 state initialState
同步修改 mutations reducers
异步操作 actions createAsyncThunk + extraReducers
模块化 modules 多个 slices + combineReducers
派生状态 getters 自定义 selector hooks
组件绑定 mapStatemapActions useSelectoruseDispatch

安装

react-redux是状态管理工具,@reduxjs/toolkit是redux的工具包(官方大力支持,代码量更小,使用更方便)

npm i react-redux @reduxjs/toolkit

redux-persist 是数据持久化工具,可以存到本地

redux-persist-transform-encrypt  是数据加密的方法

npm i redux-persist redux-persist-transform-encrypt

使用:

1、main.jsx中注册下

import { createRoot } from 'react-dom/client'

import App from './App.jsx'

// 导入合并好的stores
import stores from './stores'

// 导入reduxProvider组件传递store
import { Provider } from 'react-redux'


createRoot(document.getElementById('root')).render(
    //使用redux的元素包起来,也可以放app.jsx中
  <Provider store={stores}>
    <App />
  </Provider>
)

2、stores中,注册多个文件

类似vuex中得modules

index.js,可以引入多个,然后通过reducer来合并

import { configureStore } from "@reduxjs/toolkit";
import user from './user'
import shop from './shop'

export default configureStore({
    reducer:{
        user,
        shop
    }
})

3、user.js

initialState 里面定义的变量跟vuex里面定义变量state一样,跟vue的data定义变量一样

reducers里面定义的方法函数跟vuex里面mutations一样

import { createSlice } from '@reduxjs/toolkit'

// 创建userSlice切片,存储关于user的数据和修改user数据的方法action
const userSlice = createSlice({
    name: 'user-slice',// 每个切片的名字
    initialState: ({ // 定义数据
        user: {
            name: '张三',
            age: 18,
            gender: '男'
        }
    }),
    reducers: { // 定义方法集合
        setName: (state, action) => {
            state.user.name = action.payload
        },
        setAge: (state, action) => {
            state.user.age = action.payload
        },
        setGender: (state, action) => {
            state.user.gender = action.payload
        }
    }
})

// 导出修改数据的方法,在组件中使用
export const { setName, setAge, setGender } = userSlice.actions
export default userSlice.reducer  // 导出切片

4、shop.js

跟上面user一样的写法, 加了个异步请求接口的方法,这里可以监听他的不同状态

等同于vuex的actions异步方法

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

// 异步 action(类似 Vuex 的 actions)
export const fetchUser = createAsyncThunk(
  'user/fetchUser', // action 类型前缀
  async (userId) => {
    const response = await axios.get(`/api/users/${userId}`);
    return response.data; // 返回值作为 action.payload
  }
);

const shopSlice = createSlice({
    name: 'shop-slice',
    initialState: ({
        // 接口拿到的数据与loading状态等
        data: null, 
        loading: false, 
        error: null

        shopList: [
            {
                name: 'xiaomi 15',
                price: '3999',
                id: 1
            }
        ]
    }),
    reducers: ({
        // 同步-修改数组变量
        setName: (state, action) => {
            state.shopList[0].name = action.payload
        }
    }),
    extraReducers: (builder) => { // 处理异步 action 的不同阶段
        builder
          .addCase(fetchUser.pending, (state) => {
            state.loading = true;
          })
          .addCase(fetchUser.fulfilled, (state, action) => {
            state.data = action.payload;
            state.loading = false;
          })
          .addCase(fetchUser.rejected, (state, action) => {
            state.error = action.error.message;
            state.loading = false;
          });
  },
})

export const { setName} = shopSlice.actions

export default shopSlice.reducer

5、到这一步,可以直接在界面用了,比如demo.jsx中

dispatch(setAge() )   这就是调用同步方法 的使用,类似调用vuex里面的mutation方法

dispatch(fetchUser(12333))  调用异步方法的使用,类似调用vuex里面actions里面方法

import React from 'react'
import { useSelector, useDispatch } from 'react-redux' //固定使用
import { setAge } from '@/stores/user' //对应export 的对象 里面的函数方法名

export default function Child() {
  const user = useSelector(state => state.user.user) //直接拿到定义的变量
  const dispatch = useDispatch()
  return (
    <div>
      <h1>用户:{JSON.stringify(user)}</h1>
      <button onClick={()=>dispatch(setAge(user.age+1))}>年龄加一</button>
    </div>
  )
}
//异步调用接口
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';

import { fetchUser } from '@/store/slices/user';
import { useUser } from '@/store/hooks';

function UserProfile({ userId }) {
  const dispatch = useDispatch();
  const { data, loading, error } = useUser(); // 类似 mapState

  useEffect(() => {
    dispatch(fetchUser(userId)); // 类似 Vuex 的 actions
  }, [userId]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return <div>Username: {data?.name}</div>;
}

6、正常项目中不会用这么简单方法,还可以定义类型,以及封装hook

比如类型的type

export interface RootState{
  key: string
  path: string
  title: string
}

然后使用类型,以及发送上面定义变量的hook

import { useSelector } from 'react-redux';
import { RootState } from './type';

// 封装自定义 selector hooks,统一在这个文件,界面用的时候统一从这个文件取,不用单独引入
export const useUser = () => useSelector((state: RootState) => state.user);
export const useCounter = () => useSelector((state: RootState) => state.counter);


// 计算属性(类似 Vuex 的 getters)
export const useDoubleCount = () =>
  useSelector((state: RootState) => state.counter.value * 2);

7、第6步里面的类型定义是自定义用interface定义类型,也可以这样搞

在store/index里面
// 导出类型化的 hooks
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
然后再hook里面,接收他的定义好的类型,不用我们自定义去写
import type { RootState } from './index';

8、如果使用了数据持久化方法redux-persist

只需要在store/index文件里

// store/index.js
import { configureStore } from '@reduxjs/toolkit';

import { persistStore, persistReducer } from 'redux-persist';

import storage from 'redux-persist/lib/storage';
//import sessionStorage from 'redux-persist/lib/storage/session';

import authReducer from './slices/authSlice';//分片,其它文件

const persistConfig = {
  key: 'auth', //存储的key
  storage, //默认是localStorage 
};
const persistedReducer = persistReducer(persistConfig, authReducer);//存起来


export const store = configureStore({
  reducer: {
    auth: persistedReducer,
  },
});

export const persistor = persistStore(store);//最后发送的时候,把缓存也带上

9、如果使用了加密encrypt插件

解密是自动完成的,注意一点,如果旧数据加密存在本地,新数据修改了key,那需要删除缓存内容

import { createStore } from 'redux';

import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import encryptTransform from 'redux-persist-transform-encrypt';


const persistConfig = {
  key: 'root',
  storage,
  transforms: [
    encryptTransform({
      secretKey: 'my-super-secret-key',// 加密key
      onError: (err) => console.error('加密失败:', err),
    }),
  ],
};


网站公告

今日签到

点亮在社区的每一天
去签到