【实战】 九、深入React 状态管理与Redux机制(五) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十)

这篇具有很好参考价值的文章主要介绍了【实战】 九、深入React 状态管理与Redux机制(五) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


学习内容来源:React + React Hook + TS 最佳实践-慕课网


相对原教程,我在学习开始时(2023.03)采用的是当前最新版本:

版本
react & react-dom ^18.2.0
react-router & react-router-dom ^6.11.2
antd ^4.24.8
@commitlint/cli & @commitlint/config-conventional ^17.4.4
eslint-config-prettier ^8.6.0
husky ^8.0.3
lint-staged ^13.1.2
prettier 2.8.4
json-server 0.17.2
craco-less ^2.0.0
@craco/craco ^7.1.0
qs ^6.11.0
dayjs ^1.11.7
react-helmet ^6.1.0
@types/react-helmet ^6.1.6
react-query ^6.1.0
@welldone-software/why-did-you-render ^7.0.1
@emotion/react & @emotion/styled ^11.10.6

具体配置、操作和内容会有差异,“坑”也会有所不同。。。


一、项目起航:项目初始化与配置

  • 一、项目起航:项目初始化与配置

二、React 与 Hook 应用:实现项目列表

  • 二、React 与 Hook 应用:实现项目列表

三、TS 应用:JS神助攻 - 强类型

  • 三、 TS 应用:JS神助攻 - 强类型

四、JWT、用户认证与异步请求

  • 四、 JWT、用户认证与异步请求(上)

  • 四、 JWT、用户认证与异步请求(下)

五、CSS 其实很简单 - 用 CSS-in-JS 添加样式

  • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式(上)

  • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式(下)

六、用户体验优化 - 加载中和错误状态处理

  • 六、用户体验优化 - 加载中和错误状态处理(上)

  • 六、用户体验优化 - 加载中和错误状态处理(中)

  • 六、用户体验优化 - 加载中和错误状态处理(下)

七、Hook,路由,与 URL 状态管理

  • 七、Hook,路由,与 URL 状态管理(上)

  • 七、Hook,路由,与 URL 状态管理(中)

  • 七、Hook,路由,与 URL 状态管理(下)

八、用户选择器与项目编辑功能

  • 八、用户选择器与项目编辑功能(上)

  • 八、用户选择器与项目编辑功能(下)

九、深入React 状态管理与Redux机制

1&2

  • 九、深入React 状态管理与Redux机制(一)

3&4

  • 九、深入React 状态管理与Redux机制(二)

5~8

  • 九、深入React 状态管理与Redux机制(三)

9&10

  • 九、深入React 状态管理与Redux机制(四)

11.用redux-thunk管理登录状态

既然模态框使用 redux 来管理了,而 reduxcontext 是竞争关系,那可以尝试将之前管理登录状态的 context 改为 redux

新建 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)))

需要提前将 src\context\auth-context.tsx 中的 interface AuthForminitUser(视频中对应bootstrapUser) 导出

最后在 src\store\index.ts 中统一注册:

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

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

接下来使用 redux 中的 auth 相关功能替换原有 context 提供的 auth 的功能

重构需要找代码上游中集中的一个点:useAuth

修改 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 };
};

这种写法会报错 const dispatch: (...args: unknown[]) => Promise<User> = useDispatch()

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

替换为 const dispatch: AppDispatch = useDispatch() 后正常

useMount(async () => run(dispatch(await initUser()))) 中不加 async..await 的话会有如下报错提示:

没有与此调用匹配的重载。
  第 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"?

最终运行结果:登录,注册,登出功能都正常,只有登录保持不正常


部分引用笔记还在草稿阶段,敬请期待。。。文章来源地址https://www.toymoban.com/news/detail-640763.html

到了这里,关于【实战】 九、深入React 状态管理与Redux机制(五) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包