react redux Toolkit 分组更推荐

bash 复制代码
import { message } from 'antd';
import service from '@/utils/request2';
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
import Cookies from 'js-cookie';
import { Result } from '@/interface/ResultInterface';


interface userInterface {
  token: string;
  username: string;
  isAdmin: string;
  id: string;
}




// 异步Thunk:登录并获取用户信息
export const loginAndFetchUser = createAsyncThunk(
  'auth/loginAndFetchUser',
  async (loginData: any, { rejectWithValue }) => {
    try {
      // 1. 调用登录接口
      const loginResponse: Result<userInterface> = await service.post('/auth/login', loginData);
      const { token, username, isAdmin, id } = loginResponse.data;

      // 2. 将token存储到localStorage和Redux state

      // 3. 使用token获取用户详细信息
      // const userInfoResponse = await getUserProfile(token);
      return { token, username, isAdmin, id };
    } catch (error) {
      return rejectWithValue(error.response.data);
    }
  }
);
// 2. 提供初始状态,并明确指定其类型
const initialState: userInterface = {
  token: Cookies.get('header-access-token-dianchi') || null,
  username: Cookies.get('username') || null,
  isAdmin: Cookies.get('isAdmin') || null,
  id: Cookies.get('id') || null,
};
const userStore = createSlice({
  name: 'user',
  initialState,
  reducers: {
    logout: (state) => {
      state.token = null;
      state.username = null;
      Cookies.remove('username');
      Cookies.remove('header-access-token-dianchi');
      Cookies.remove('isAdmin');
      Cookies.remove('id');
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(loginAndFetchUser.pending, (state) => { })
      .addCase(loginAndFetchUser.fulfilled, (state, action) => {
        state.token = action.payload.token;
        state.username = action.payload.username;
        state.isAdmin = action.payload.isAdmin.toString();
        state.id = action.payload.id.toString();


        Cookies.set('header-access-token-dianchi', state.token);
        Cookies.set('username', state.username);
        Cookies.set('isAdmin', state.isAdmin);
        Cookies.set('id', state.id);
      })
      .addCase(loginAndFetchUser.rejected, (state, action) => { });
  },
});
//解构出action对象的函数
export const { logout } = userStore.actions;
//获取reducer函数
const userReducer = userStore.reducer;
export default userReducer;
bash 复制代码
import { MenuItem } from '@/types/MenuItem.type';
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

export interface MenuState {
  menuList: MenuItem[]; // menuList 应该是 MenuItem 类型的数组
  // 如果你有其他状态字段,也在这里定义,例如:
  isHasMenu: boolean;
}

// 2. 提供初始状态,并明确指定其类型
const initialState: MenuState = {
  menuList: [], // 初始为空数组,符合 MenuItem[] 类型
  isHasMenu: false,
};
const menuStore = createSlice({
  name: 'menu',
  initialState,
  reducers: {
    setIsHasMenu(state: MenuState) {
      state.isHasMenu = true;
    },
    // 示例1:设置整个菜单列表]
    setMenuList(state: MenuState, action: PayloadAction<MenuItem[]>) {
      state.menuList = action.payload;
    },
    // 示例2:添加单个菜单项
    addMenuItem(state: MenuState, action: PayloadAction<MenuItem>) {
      state.menuList.push(action.payload);
    },
    // 示例3:根据ID移除菜单项
    removeMenuItem(state: MenuState, action: PayloadAction<string>) {
      // 假设ID是字符串类型
      state.menuList = state.menuList.filter((item: MenuItem) => item.path !== action.payload);
    },
  },
});
//解构出action对象的函数
const { setMenuList, addMenuItem, removeMenuItem,setIsHasMenu } = menuStore.actions;
//获取reducer函数
const menuReducer = menuStore.reducer;
export { setIsHasMenu,setMenuList, addMenuItem, removeMenuItem };
export default menuReducer;
bash 复制代码
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

export interface ThemeState {
  theme: 'light' | 'dark';
}

// 从 localStorage 读取主题设置,默认为 light
const getInitialTheme = (): 'light' | 'dark' => {
  const savedTheme = localStorage.getItem('theme');
  return (savedTheme === 'dark' || savedTheme === 'light') ? savedTheme : 'light';
};

const initialState: ThemeState = {
  theme: getInitialTheme()
};

const themeSlice = createSlice({
  name: 'theme',
  initialState,
  reducers: {
    toggleTheme: (state) => {
      state.theme = state.theme === 'light' ? 'dark' : 'light';
      localStorage.setItem('theme', state.theme);
      // 更新 document 的 data-theme 属性
      document.documentElement.setAttribute('data-theme', state.theme);
    },
    setTheme: (state, action: PayloadAction<'light' | 'dark'>) => {
      state.theme = action.payload;
      localStorage.setItem('theme', state.theme);
      // 更新 document 的 data-theme 属性
      document.documentElement.setAttribute('data-theme', state.theme);
    }
  }
});

// 导出 actions
export const { toggleTheme, setTheme } = themeSlice.actions;

// 导出 reducer
const themeReducer = themeSlice.reducer;
export default themeReducer;
bash 复制代码
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './slice/counterSlice';
import menuReducer from './slice/menuSlice';
import userReducer from './slice/userSlice';
import themeReducer from './slice/themeSlice';

const store = configureStore({
    reducer: {
        counterReducer: counterReducer,
        menuReducer:menuReducer,
        userReducer:userReducer,
        themeReducer:themeReducer,
    }
})
export type AppDispatch = typeof store.dispatch; // 导出 Dispatch 类型,用于异步 Action
export default store;

使用

bash 复制代码
import React, { useState } from 'react';
import { Form, Input, Button, message } from 'antd';
import { UserOutlined, LockOutlined } from '@ant-design/icons';
import './index.css';   // 样式见下方
import { useNavigate } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { loginAndFetchUser } from '@/store/slice/userSlice';
import { useSelector } from 'react-redux';
const Login = () => {
  const [loading, setLoading] = useState(false);
  const navigate = useNavigate();
  const dispatch = useDispatch<any>();
  const theme = useSelector((state: any) => state.themeReducer.theme);
  const onFinish = async ({ account, password }) => {
    let params = {
      account: account,
      password,
    };
    try {
      try {
        await dispatch(loginAndFetchUser(params)).unwrap();
        navigate('/');
        // 登录成功后的跳转逻辑在下面的useEffect中处理
      } catch (error) {
        // 错误信息已在Redux中存储,这里可以显示错误提示
        console.error('Login failed:', error);
      }
    } catch (error) {
      navigate('/');
    }
  };

  return (
    <div className="login-wrapper">
      <div className="login-card">
        <h1 className="login-title">电池管理项目</h1>
        <Form
          name="login"
          layout="vertical"
          onFinish={onFinish}
          autoComplete="off"
          initialValues={{
            account: 'admin',
            password: '123456',
          }}
        >
          <Form.Item
            name="account"
            rules={[{ required: true, message: '请输入账户' }]}
          >
            <Input
              prefix={<UserOutlined />}
              placeholder="账户 / Email"
              size="large"
            />
          </Form.Item>

          <Form.Item
            name="password"
            rules={[{ required: true, message: '请输入密码' }]}

          >
            <Input.Password
              prefix={<LockOutlined />}
              placeholder="密码"
              size="large"
            />
          </Form.Item>

          <Form.Item>
            <Button
              type="primary"
              htmlType="submit"
              loading={loading}
              size="large"
              block
              className="login-btn"
            >
              立 即 登 录
            </Button>
          </Form.Item>
        </Form>
      </div>
    </div>
  );
};

export default Login;
相关推荐
openKaka_14 分钟前
createRoot 到底创建了什么:FiberRootNode 和 HostRootFiber 的初始化过程
前端·javascript·react.js
习明然1 小时前
UniApp开发体验感受总结
前端·uni-app
刀法如飞2 小时前
Claude Code Skills 推荐:2026年最值得安装的10个AI技能
前端·后端·ai编程
阿豪只会阿巴2 小时前
【没事学点啥】TurboBlog轻量级个人博客项目——项目介绍
javascript·python·django·html
Lee川2 小时前
面试手写 KeepAlive:React 组件缓存的实现原理
前端·react.js·面试
墨染天姬2 小时前
【AI】cursor提示词小技巧
前端·数据库·人工智能
烛阴2 小时前
TEngine 入门系列(一):TEngine 是什么 & 为什么选它
前端·unity3d
转转技术团队3 小时前
WebNN:让 AI 推理在浏览器中“零距离”运行
前端
刀法如飞3 小时前
TypeScript 数组去重的 20 种实现方式,哪一种你还不知道?
前端·javascript·算法
IT_陈寒3 小时前
Vite热更新失效?你可能漏了这个小细节
前端·人工智能·后端