[Práctica] 9. Gestión detallada del estado de React y mecanismo Redux (5) —— Práctica recomendada de React17+React Hook+TS4, que imita los proyectos de nivel empresarial de Jira (20)


Fuente del contenido de aprendizaje: React + React Hook + TS Best Practice - MOOC


En comparación con el tutorial original, utilicé la última versión al comienzo de mi estudio (2023.03):

artículo Versión
reaccionar y reaccionar-dom ^18.2.0
reaccionar-router y reaccionar-router-dom ^6.11.2
y ^4.24.8
@commitlint/cli & @commitlint/config-convencional ^17.4.4
eslint-config-prettier ^8.6.0
fornido ^8.0.3
pelusa puesta en escena ^13.1.2
más bonita 2.8.4
servidor json 0.17.2
craco-menos ^2.0.0
@craco/craco ^7.1.0
qs ^6.11.0
diajs ^ 1.11.7
reaccionar-casco ^6.1.0
@types/reaccionar-casco ^6.1.6
reacción-consulta ^6.1.0
@welldone-software/por-que-rendiste ^7.0.1
@emoción/reaccionar & @emoción/estilo ^11.10.6

La configuración específica, la operación y el contenido serán diferentes, y el "pozo" también será diferente. . .


1. Lanzamiento del proyecto: inicialización y configuración del proyecto

2. Aplicación React and Hook: implementa la lista de proyectos

3. Aplicación TS: JS God Assist - Tipo fuerte

4. JWT, autenticación de usuario y solicitud asíncrona


5. CSS es realmente muy simple: agregue estilos con CSS-in-JS


6. Optimización de la experiencia del usuario: carga y manejo del estado de error



7. Gestión de enlace, enrutamiento y estado de URL



8. Selector de usuario y función de edición de elementos.


9. Gestión detallada del estado React y mecanismo Redux

1 y 2

3 y 4

5~8

9 y 10

11. Administrar el estado de inicio de sesión con redux-thunk

Dado que el cuadro modal está reduxadministrado por y reduxtiene una relación competitiva con , puede intentar cambiar contextel estado de inicio de sesión administrado anteriormente acontextredux

nuevo src\store\auth.slice.ts:

import {
    
     User } from "screens/ProjectList/components/SearchPanel";
import {
    
     createSlice } from "@reduxjs/toolkit";
import * as auth from 'auth-provider'
import {
    
     AuthForm, initUser as _initUser } from "context/auth-context";
import {
    
     AppDispatch, RootState } from "store";

interface State {
    
    
  user: User | null
}

const initialState: State = {
    
    
  user: null
}

export const authSlice = createSlice({
    
    
  name: 'auth',
  initialState,
  reducers: {
    
    
    setUser(state, action) {
    
    
      state.user = action.payload
    }
  }
})

const {
    
     setUser } = authSlice.actions

export const selectUser = (state: RootState) => state.auth.user

export const login = (form: AuthForm) => (dispatch: AppDispatch) => auth.login(form).then(user => dispatch(setUser(user)))
export const register = (form: AuthForm) => (dispatch: AppDispatch) => auth.register(form).then(user => dispatch(setUser(user)))
export const logout = () => (dispatch: AppDispatch) => auth.logout().then(() => dispatch(setUser(null)))
export const initUser = () => (dispatch: AppDispatch) => _initUser().then(user => dispatch(setUser(user)))

Es necesario exportar el y (correspondiente en el video ) src\context\auth-context.tsxcon anterioridadinterface AuthForminitUserbootstrapUser

Por último, src\store\index.tsunificar el registro en:

...
import {
    
     authSlice } from "./auth.slice";

// 集中状态注册
export const rootReducer = {
    
    
  projectList: projectListSlice.reducer,
  auth: authSlice.reducer
};
...

A continuación, use las funciones relevantes reduxpara reemplazar las funciones proporcionadas por el originalauthcontextauth

La refactorización necesita encontrar un punto centralizado en el código ascendente: useAuth

modificar src\context\auth-context.tsx:

+ import { ReactNode, useCallback } from "react";
...
+ import * as authStore from 'store/auth.slice'
+ import { useDispatch, useSelector } from "react-redux";
+ import { AppDispatch } from "store";

- interface AuthForm {
+ export interface AuthForm {
  username: string;
  password: string;
}

- const initUser = async () => {
+ export const initUser = async () => {
  let user = null;
  const token = auth.getToken();
  if (token) {
    // 由于要自定义 token ,这里使用 http 而非 useHttp
    const data = await http("me", { token });
    user = data.user;
  }
  return user
};

- const AuthContext = React.createContext<
-   | {
-       user: User | null;
-       login: (form: AuthForm) => Promise<void>;
-       register: (form: AuthForm) => Promise<void>;
-       logout: () => Promise<void>;
-     }
-   | undefined
- >(undefined);

- AuthContext.displayName = "AuthContext";

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  // 这里要考虑到初始值的类型与后续值类型,取并组成一个泛型
  const {
-     data: user,
    error,
    isLoading,
    isReady,
    isError,
    run,
-     setData: setUser,
  } = useAsync<User | null>();
+   // const dispatch: (...args: unknown[]) => Promise<User> = useDispatch()
+   // const dispatch: AppDispatch = useDispatch()
+ 
+   // 这种写法虽然消除了代码中的报错,但是控制台会有报错
+   // useMount(async () => run(dispatch(await initUser())));
+   // useMount(() => run(dispatch(initUser())));
+   // 还原后登录保持功能已经是不好使的了
  useMount(() => run(initUser()));
  
-   const login = (form: AuthForm) => auth.login(form).then(setUser);
-   const register = (form: AuthForm) => auth.register(form).then(setUser);
-   const logout = () => auth.logout().then(() => setUser(null));

  if (isReady || isLoading) {
    return <FullPageLoading />;
  }

  if (isError) {
    return <FullPageErrorFallback error={error} />;
  }

-   return (
-     <AuthContext.Provider
-       children={children}
-       value={
   
   { user, login, register, logout }}
-     />
-   );
+   return <div>
+     { children }
+   </div>
};

export const useAuth = () => {
-   const context = React.useContext(AuthContext);
-   if (!context) {
-     throw new Error("useAuth 必须在 AuthProvider 中使用");
-   }
-   return context;
+   // 这种写法有报错
+   // const dispatch: (...args: unknown[]) => Promise<User> = useDispatch()
+   const dispatch: AppDispatch = useDispatch()
+   const user = useSelector(authStore.selectUser)
+   const login = useCallback((form: AuthForm) => dispatch(authStore.login(form)), [dispatch])
+   const register = useCallback((form: AuthForm) => dispatch(authStore.register(form)), [dispatch])
+   const logout = useCallback(() => dispatch(authStore.logout()), [dispatch])
+   // const login = useCallback((form: AuthForm) => authStore.login(form), [])
+   // const register = useCallback((form: AuthForm) => authStore.register(form), [])
+   // const logout = useCallback(() => authStore.logout(), [])
+   // login({ username: '123', password: '123' }).then()
+   return { user, login, register, logout };
};

Esta forma de escribir reportará un error const dispatch: (...args: unknown[]) => Promise<User> = useDispatch():

不能将类型“Dispatch<AnyAction>”分配给类型“(...args: unknown[]) => Promise<User>”。
  参数“action”和“args” 的类型不兼容。
    不能将类型“unknown”分配给类型“AnyAction”

const dispatch: AppDispatch = useDispatch()Normal después de reemplazar con

useMount(async () => run(dispatch(await initUser())))Si no agrega , async..awaitrecibirá el siguiente mensaje de error:

没有与此调用匹配的重载。
  第 1 个重载(共 3 个),“(thunkAction: ThunkAction<Promise<User | null>, { projectList: State; auth: State; }, undefined, AnyAction>): Promise<User | null>”,出现以下错误。
    类型“Promise<any>”的参数不能赋给类型“ThunkAction<Promise<User | null>, { projectList: State; auth: State; }, undefined, AnyAction>”的参数。
      类型“Promise<any>”提供的内容与签名“(dispatch: ThunkDispatch<{ projectList: State; auth: State; }, undefined, AnyAction>, getState: () => { projectList: State; auth: State; }, extraArgument: undefined): Promise<...>”不匹配。
  第 2 个重载(共 3 个),“(action: AnyAction): AnyAction”,出现以下错误。
    类型“Promise<any>”的参数不能赋给类型“AnyAction”的参数。
      类型 "Promise<any>" 中缺少属性 "type",但类型 "AnyAction" 中需要该属性。
  第 3 个重载(共 3 个),“(action: AnyAction | ThunkAction<Promise<User | null>, { projectList: State; auth: State; }, undefined, AnyAction>): AnyAction | Promise<...>”,出现以下错误。
    类型“Promise<any>”的参数不能赋给类型“AnyAction | ThunkAction<Promise<User | null>, { projectList: State; auth: State; }, undefined, AnyAction>”的参数。
      不能将类型“Promise<any>”分配给类型“AnyAction”。ts(2769)
auth-context.tsx(41, 31): 是否忘记使用 "await"?
index.d.ts(19, 3): 在此处声明了 "type"。
auth-context.tsx(41, 31): 是否忘记使用 "await"?
auth-context.tsx(41, 31): 是否忘记使用 "await"?

Resultado final de ejecución: las funciones de inicio de sesión, registro y cierre de sesión son todas normales, solo el inicio de sesión sigue siendo anormal


Algunas notas de referencia aún están en etapa de borrador, así que permanezca atento. . .

Supongo que te gusta

Origin blog.csdn.net/qq_32682301/article/details/132168815
Recomendado
Clasificación