状态管理
组件嵌套层级很多,props层层传递不合适,使用状态管理进行集中、统一管理页面数据
状态提升
Context 跨层级传递
切换主题、语言
useContext()
export const ThemeContext=createContext(themes.light)
<ThemeContext.Provider value={themes.light}>
const theme=useContext(ThemeContext)
useReducer
简化的 redux,复杂数据结构
根据传入的 action 返回新的 state
function reducer(state:StateType,action:ActionType){
switch(action.type){
case 'add':
return { count: state.count + 1 }
default:
throw new Error()
}
}
// use
const [state, dispatch]=useReducer(reducer,initialState)
return (
<button onClick={()=> dispatch({ type:'add' })} />
)
state action reducer dispatch
使用 Context + useReducer(state ,dispatch)实现 todoList 组件开发
不能模块化
Redux
功能完善,可拆分模块
// ---------- count.ts ----------
import {createSlice} from '@reduxjss/toolkit'
const INIT_STATE: number = 100
export const countSlice=createSlice({
name: 'count',
initialState: INIT_STATE,
reducers: {
add(state: number){
return state + 1
},
lower(state: number){
return state - 1
}
}
})
export const {add,lower} = countSlice.actions
export default countSlice.reducer
// ---------- index.ts ----------
export type StateType = {
count: number
}
export default configureStore({
reducer: {
count: countReducer
// 其他模块
}
})
// ---------- use ----------
const count= useSelector<StateType>(state =>state.count)
const dispatch = useDispatch()
<button onClick={()=>dispatch(add())} />+
使用 redux 重新实现 todoList nanoid 随机数
单向数据流
<Provider> store reducer action dispatch 配合immer
MobX 声明式的修改数据
state action derivation computed observable
class Timer {
time=''
reset(){ }
constructor() {
makeAutoObservable(this,{
time: observable,
reset: action
})
}
}
store.ts 定义 ObservableTodoStore 和 ObservableTodoListStore class,在constructor()中定义makeObservable(this,{})
index.tsx 页面主组件
TodoList.tsx 渲染出列表
TodoView.tsx 显示todo
管理用户信息
src/store/userReducer.ts
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: UserStateType,
action: PayloadAction<UserStateType>
) => {
return action.payload; // 设置 username、nickname 到 redux store
},
logoutReducer: () => INIT_STATE, // 重置 state
},
});
export const { loginReducer, logoutReducer } = userSlice.actions;
export default userSlice.reducer;
src/store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import userReducer,{ UserStateType } from './userReducer'
export type StateType={
user:UserStateType
}
export default configureStore({
reducer:{
// user
user: userReducer
}
})
main.tsx
<Provider store={store}>
<Edit/>
</Provider>
使用: 三个 hook useGetUserInfo
useLoadUserData
import { useState, useEffect } from "react";
import { useRequest } from 'ahooks'
import { useDispatch } from "react-redux";
//
import useGetUserInfo from "./useGetUserInfo";
import { getUserInfoService } from '@/services/user'
import { loginReducer } from '@/store/userReducer'
function useLoadUserData() {
// ajax请求用户数据后,直接放在 redux中
// 这里不需要再返回数据了
const [waitingUserData, setWaitingUserData] = useState(true);
const dispatch=useDispatch()
// ajax
const { run }=useRequest(getUserInfoService,{
manual: true,
onSuccess(res){
const {username,nickname}=res
dispatch(loginReducer({username,nickname}))
},
onFinally(){
setWaitingUserData(false)
}
})
const { username } =useGetUserInfo()
useEffect(()=>{
if(username){
setWaitingUserData(false)
return
}
run()
},[username])
return { waitingUserData };
}
export default useLoadUserData;
useNavPage
import { useEffect } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
//
import useGetUserInfo from './useGetUserInfo'
import { ROUTE_PATH, isLoginOrRegister, isNoNeedUserInfo } from '@/router'
function useNavPage(waitingUserData:boolean){
const { username }=useGetUserInfo()
const { pathname}=useLocation()
const nav =useNavigate()
useEffect(()=>{
if(waitingUserData)
return
// already login
if(username){
if(isLoginOrRegister(pathname)){
nav(ROUTE_PATH.QUESTION_EDIT)
}
return
}
// not login
if(isNoNeedUserInfo(pathname)){
return
}else{
nav(ROUTE_PATH.LOGIN)
}
},[waitingUserData,username,pathname])
}
export default useNavPage