Directorio de artículos
-
- 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
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á redux
administrado por y redux
tiene una relación competitiva con , puede intentar cambiar context
el estado de inicio de sesión administrado anteriormente acontext
redux
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.tsx
con anterioridadinterface AuthForm
initUser
bootstrapUser
Por último, src\store\index.ts
unificar el registro en:
...
import {
authSlice } from "./auth.slice";
// 集中状态注册
export const rootReducer = {
projectList: projectListSlice.reducer,
auth: authSlice.reducer
};
...
A continuación, use las funciones relevantes redux
para reemplazar las funciones proporcionadas por el originalauth
context
auth
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..await
recibirá 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. . .