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;
相关推荐
笨笨狗吞噬者2 小时前
uni-app 运行时揭秘:styleIsolation 的转化
前端·微信小程序·uni-app
是席木木啊2 小时前
告别console.log!Vue3项目日志框架选型指南
前端·vue3·日志框架
凰轮2 小时前
TypeScript 知识点总结
前端·javascript·typescript
Hello--_--World3 小时前
JS:闭包、函数柯里化、工厂函数、偏函数、立即执行函数 相关知识点与面试题
开发语言·javascript·ecmascript
英俊潇洒美少年3 小时前
告别大请求卡顿!原生 CompressionStream 实现 axios 请求体自动 Gzip 压缩(前后端全适配)
前端
|晴 天|3 小时前
Element Plus 组件库实战技巧与踩坑记录
前端·javascript·vue.js·typescript
im_AMBER3 小时前
从面试题看JS变量提升
开发语言·前端·javascript·前端框架
Mintopia3 小时前
不用学微服务,也能设计不崩的系统:最小可行思路
前端
llf_cloud3 小时前
Vue2 项目中的全局自动弹窗队列设计
前端·javascript·vue.js