目标
学会react 状态管理工具
 使用redux管理用户状态
Context
- 跨层级传递,不像props层层传递
- 类似于Vue的provide/inject
- 用于:切换主题颜色,切换语言
useReducer
useState 的替代方案
 简化版的redux
MobX
1. MobX 介绍 · MobX 中文文档
声明式的修改数据 , 像vue
 state
 action
 derivation 派生: computed observer
Redux
- state/store (存储的数据)
- action(发布的命令 类似导航让我们知道发生了什么)
- reducer(生成新的state 联系起state与action)
- dispatch(派发的action,产生数据)
默认支持跨组件通讯
项目实战–redux
快速开始 | Redux 中文官网
Redux 要求我们通过创建数据副本和更新数据副本,来实现不可变地写入所有状态更新。不过 Redux Toolkit createSlice 和 createReducer 在内部使用 Immer 允许我们编写“可变”的更新逻辑,变成正确的不可变更新。
引入redux
安装
npm install @reduxjs/toolkit react-redux
为 React 提供 Redux Store
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import { Provider } from 'react-redux'
import store from './store/index.ts'
ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
)
创建Redux Store
import { configureStore } from '@reduxjs/toolkit'
import userReducer from './userReducer'
export default configureStore({
  reducer: {
    //分模块注册
    user: userReducer, // 注册userReducer
  
    // 你可以在这里注册更多的reducer
  }
})
创建 Redux State Slice
import { createSlice , PayloadAction } from "@reduxjs/toolkit";
  
export type UserStateType={
    username:string,
    nickname:string
}
  
  
const INIT_STATE:UserStateType={
    username:"",
    nickname:""
}
  
  
export const userSlice=createSlice({
    name:"user",
    initialState:INIT_STATE,
    reducers:{
        //登录保存用户信息
        loginReducer:(state,action:PayloadAction<UserStateType>)=>{
            return action.payload // 设置username nickname 到 redux store
            //用不到immer
  
        },
        //退出删除用户信息
        logoutReducer:()=>{
            return INIT_STATE // 设置username nickname 到 redux store
            //用不到immer
        }
    }
})
  
  
export const {loginReducer,logoutReducer}=userSlice.actions
export default userSlice.reducer
使用

用户信息存储
自定义hook
import { useEffect , useState } from "react";
  
import { useRequest } from "ahooks";
//导入发起请求的函数
  
import { useDispatch } from "react-redux";
import { getUserInfoService } from "../services/user";
import { loginReducer } from "../store/userReducer";
import useGetUserInfo from "./useGetUserInfo";
  
function useLoadUserData() {
    const dispatch = useDispatch();
    const [waitingUserData , setWaitingUserData] = useState(true);
  
   //ajax 加载用户信息
   const {run } = useRequest(getUserInfoService , {
       manual:true,
       onSuccess: (data) => {
         const {username , nickname} = data;
         dispatch(loginReducer({
            username,
            nickname
         })) // 存储到redux store
       },
       onFinally(){
        setWaitingUserData(false);
       }
   })
  
  
  
   //判断当前的redux  store 是否存在用户信息
   const {username} = useGetUserInfo()
   useEffect(() => {
       if(username){
           setWaitingUserData(false); // redux中存在信息,则不用重新加载
           return ;
       }
       run();// 如果不存在,则进行加载
   },[username])
  
  
   return {waitingUserData};
  
}
  
  
export default useLoadUserData;

由于后端有延迟,首页闪了一下,前端使用spin组件(显示加载效果)

bug :用户登录成功后.还能访问登录注册页
预期效果:
 已经登录
 1. 跳转页面是登录注册页 , 重定向到我的问卷
 2. 跳转页面不是登录注册页,放行
 未登录
 1. 跳转页面需要用户信息,重定向到登录页
 2. 跳转页面不需要用户信息,放行
自定义hook
import React , {FC, useEffect} from "react";
import useGetUserInfo from "./useGetUserInfo";
import { useNavigate ,useLocation } from "react-router-dom";
import useLoadUserData from "./useLoadUserData";
import { isLoginOrRegiter, isNeedUserInfo, LOGIN_PATH, MANAGE_PATH } from "../router";
  
function useNavPage() {
    const {waitingUserData} = useLoadUserData();
    const {username} = useGetUserInfo();
    const {pathname} = useLocation();
  
    const nav = useNavigate();
    useEffect(()=>{
        if(waitingUserData) return ;
  
        //已经登录了
        if(username){
            //跳转的页面是login/register
            if(isLoginOrRegiter(pathname))
             {
                nav(MANAGE_PATH)
             }
            return
        }
  
        //没有登录
        if(isNeedUserInfo(pathname))
        {
            nav(LOGIN_PATH);
        }
  
  
  
    },[username,pathname])
  
}
  
  
  
export default useNavPage;

总结
自定义hook使用
 useGetUserInfo
 useLoadUserData
 useNavPage



















