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

文章目录

    • 一、项目起航:项目初始化与配置
    • 二、React 与 Hook 应用:实现项目列表
    • 三、TS 应用:JS神助攻 - 强类型
    • 四、JWT、用户认证与异步请求
    • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式
    • 六、用户体验优化 - 加载中和错误状态处理
    • 七、Hook,路由,与 URL 状态管理
    • 八、用户选择器与项目编辑功能
    • 九、深入React 状态管理与Redux机制
      • 1&2
      • 3.合并组件状态,实现useUndo
      • 4.用useReducer进行状态管理


学习内容来源: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
prettier2.8.4
json-server0.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.合并组件状态,实现useUndo

功能描述:

可以对一个数字进行不断地赋值,同时记录下历史值;可以通过undo对当前值进行撤销操作,一步步地回到最初值。在进行撤销操作的同时,记录下undo掉的值;通过redo可以回到undo之前的值,不断地redo最终可以回到执行所有撤销操作之前的值。

代码实现

使用useState实现该hook,由于多个state值之前相互引用。因此useCallback的依赖项中会填入多个state作为依赖项,但是在useCallback的回调函数中,在调用时会更新state值导致页面重新渲染,useCallback的回调函数也被重新定义了。

接下来模仿实现 use-undo

  • use-undo-demo - CodeSandbox
  • use-undo - npm

新建 src\utils\use-undo.ts

import { useState } from "react";export const useUndo = <T>(initialPresent: T) => {// 记录历史操作的合集const [past, setPast] = useState<T[]>([])const [present, setPresent] = useState(initialPresent)// 记录未来操作的合集const [future, setFuture] = useState<T[]>([])const canUndo = past.length !== 0const canRedo = future.length !== 0const undo = () => {if (!canUndo) returnconst previous = past[past.length - 1]const newPast = past.slice(0, past.length - 1)setPast(newPast)setPresent(previous)setFuture([present, ...future])}const redo = () => {if (!canRedo) returnconst next = future[0]const newFuture = future.slice(1)setFuture(newFuture)setPresent(next)setPast([...past, present])}const set = (newPresent: T) => {if (newPresent === present) {return}setPast([...past, present])setPresent(newPresent)setFuture([])}const reset = (newPresent: T) => {setPast([])setPresent(newPresent)setFuture([])}return [{past, present, future},{redo, undo, set, reset, canRedo, canUndo}] as const
}

现需要对代码做以下优化:

  • 使用 useCallback 避免 造成之前的那种 依赖不等导致的循环渲染
  • 将用到的 变量合并声明 后续也同步改动
  • SetXXX 使用函数形式 直接使用历史状态 避免外界状态的使用,减少依赖
import { useCallback, useState } from "react";export const useUndo = <T>(initialPresent: T) => {// 合并声明const [state, setState] = useState<{past: T[],present: T,future: T[]}>({past: [],present: initialPresent,future: []})// 另一种写法// const [state, setState] = useState({//   past: [] as T[],//   present: initialPresent as T,//   future: [] as T[]// })const canUndo = state.past.length !== 0const canRedo = state.future.length !== 0const undo = useCallback(() => {setState(currentState => {const { past, present, future } = currentStateif (past.length === 0) return currentStateconst previous = past[past.length - 1]const newPast = past.slice(0, past.length - 1)return {past: newPast,present: previous,future: [present, ...future]}})}, [])const redo = useCallback(() => {setState(currentState => {const { past, present, future } = currentStateif (future.length === 0) return currentStateconst next = future[0]const newFuture = future.slice(1)return {past: [...past, present],present: next,future: newFuture}})}, [])const set = useCallback((newPresent: T) => {setState(currentState => {const { past, present } = currentStateif (newPresent === present) {return currentState}return {past: [...past, present],present: newPresent,future: []}})}, [])const reset = useCallback((newPresent: T) => {setState({past: [],present: newPresent,future: []})}, [])return [state,{redo, undo, set, reset, canRedo, canUndo}] as const
}

4.用useReducer进行状态管理

替代方案:

useReducer作为useState 的替代方案。它接收一个形如 (state, action) => newState的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。

相比较于useStateuseReducer 具有如下优点:

  • state中的状态值之间相互关联;
  • 下一个 state的更新依赖于之前的 state
  • useReducer | Hook API 索引 – React

下面使用 useReducer 再对 use-undo 进行改写

编辑 src\utils\use-undo.ts

import { useCallback, useReducer, useState } from "react";const UNDO = 'UNDO'
const REDO = 'REDO'
const SET = 'SET'
const RESET = 'RESET'type State<T> = {past: T[];present: T;future: T[];
}type Action<T> = { newPresent?: T, type: typeof UNDO | typeof REDO | typeof SET | typeof RESET }const undoReducer = <T>(state: State<T>, action: Action<T>) => {const { past, present, future } = stateconst { newPresent, type } = actionswitch(type) {case UNDO: {if (past.length === 0) return state;const previous = past[past.length - 1];const newPast = past.slice(0, past.length - 1);return {past: newPast,present: previous,future: [present, ...future],};}case REDO: {if (future.length === 0) return state;const next = future[0];const newFuture = future.slice(1);return {past: [...past, present],present: next,future: newFuture,};}case SET: {if (newPresent === present) {return state;}return {past: [...past, present],present: newPresent,future: [],};}case RESET: {return {past: [],present: newPresent,future: [],}}default: return state}
}export const useUndo = <T>(initialPresent: T) => {const [state, dispatch] = useReducer(undoReducer, {past: [],present: initialPresent,future: [],} as State<T>)const canUndo = state.past.length !== 0;const canRedo = state.future.length !== 0;const undo = useCallback(() => dispatch({ type: UNDO }), []);const redo = useCallback(() => dispatch({ type: REDO }), []);const set = useCallback((newPresent: T) => dispatch({newPresent, type: SET}), []);const reset = useCallback((newPresent: T) => dispatch({newPresent, type: RESET}), []);return [state, { redo, undo, set, reset, canRedo, canUndo }] as const;
};

统一状态管理后 虽然代码量多了,但是经过多重封装,层次更加清晰

可以发现 useReducer 相对 useState 适合定义多个相互影响的状态量

鉴于 useReducer 针对复杂的state关系和更新的前后依赖的优势,因此 useAsync 非常适合使用 useReducer 来重构

接下来使用 useReducer 改造一下 与 use-undo 结构类似的 use-async(src\utils\use-async.ts):

...
const useSafeDispatch = <T>(dispatch: (...args: T[]) => void) => {const mountedRef = useMountedRef()return useCallback((...args: T[]) => (mountedRef.current ? dispatch(...args) : void 0), [dispatch, mountedRef])
}export const useAsync = <D>(...) => {const config = { ...defaultConfig, ...initialConfig };const [state, dispatch] = useReducer((state: State<D>, action: Partial<State<D>>) => ({...state, ...action}), {...defaultInitialState,...initialState,});const safeDispatch = useSafeDispatch(dispatch);const [rerun, setRerun] = useState(() => () => {});const setData = useCallback((data: D) =>safeDispatch(...),[safeDispatch]);const setError = useCallback((error: Error) =>safeDispatch(...),[safeDispatch]);// run 来触发异步请求const run = useCallback((...) => {...safeDispatch({ stat: "loading" });return promise.then((data) => {setData(data);return data;}).catch(...);},[config.throwOnError, safeDispatch, setData, setError]);...
};

部分引用笔记还在草稿阶段,敬请期待。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/18202.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 根据图片的EXIF自动调整图片方向

PropertyItems 代码 /// <summary>/// 根据图片exif调整方向/// </summary>/// <param name"img"></param>public void RotateImage(Bitmap img){var exif img.PropertyItems;byte orien 0;var item exif.Where(m > m.Id 274).ToArra…

林园价值交易策略

文章目录 选股策略林园6条炒股“心经” 选股策略 选股时可以考虑在低市盈率、高分红的绩优龙头股和确定性高的小盘股中选。 所选择的上市公司的财务指标需符合七大标准&#xff1a; 每股盈利不低于0.3元&#xff1b;净利润不少于7000万元&#xff1b;毛利率在20%以上&#x…

关于综合能源智慧管理系统的架构及模式规划的研究

安科瑞 华楠 摘 要&#xff1a;探讨了国内外能源互联网的研究发展&#xff0c;分析了有关综合智慧能源管理系统的定位&#xff0c;以及系统的主要特点&#xff0c;研究了综合智慧能源管理系统的构架以及模式规划。 关键词&#xff1a;综合能源&#xff1b;智慧管理系统&#…

前端开发:基于cypress的自动化实践

如何在vue中使用cypress如何运行cypress如何编写测试用例如何解决测试数据的问题遇到的元素定位的问题如何看待cypresscypress是否为最佳工具测试怎么办&#xff1f; 如何在vue中使用cypress vue提供了vue-cli 可以快速的创建vue项目。 vue create hello-world在选择安装项里…

SK5代理与IP代理:网络安全中的爬虫利器

一、什么是IP代理与SK5代理&#xff1f; IP代理&#xff1a; IP代理是一种允许用户通过代理服务器进行网络连接的技术。用户请求经由代理服务器中转&#xff0c;从而实现隐藏真实IP地址&#xff0c;保护用户隐私&#xff0c;并在一定程度上突破IP访问限制。常见的IP代理有HTTP…

【李宏毅机器学习·学习笔记】Deep Learning General Guidance

本节课可视为机器学习系列课程的一个前期攻略&#xff0c;这节课主要对Machine Learning 的框架进行了简单的介绍&#xff1b;并以training data上的loss大小为切入点&#xff0c;介绍了几种常见的在模型训练的过程中容易出现的情况。 课程视频&#xff1a; Youtube&#xff1…

【MTK平台】【wpa_supplicant】关于wpa_supplicant_8/src/p2p/p2p_invitation.c文件的介绍

本文主要介绍external/wpa_supplicant_8/src/p2p/p2p_invitation.c文件 这里主要介绍6个方法 1.p2p_invite //p2p邀请调用此方法 2.p2p_invite_send //对p2p_invite方法进行补充 3. p2p_process_invitation_resp 4.p2p_process_invitation_req 5.p2p_build_invitation_re…

Java并发系列之二:悲观锁机制

什么是锁 在并发环境下&#xff0c;会出现多个线程对同一个资源进行争抢的情况&#xff0c;假设A线程对资源正在进行修改&#xff0c;此时B线程此时又对资源进行了修改&#xff0c;这就可能会导致数据不一致的问题。为了解决这个问题&#xff0c;很多编程语言引入了锁机制&…

Python的正则表达式re模块的compile()方法有什么作用?

re模块是Python标准库中的正则表达式模块&#xff0c;它提供了对正则表达式的支持。re.compile()是re模块的一个方法&#xff0c;用于将正则表达式编译成可复用的正则对象。 正则表达式是用来匹配和处理文本模式的强大工具。当你需要在字符串中查找、替换或者提取符合特定模式…

前端学习--vue2--插槽

写在前面&#xff1a; 这个用法是在使用组件和创建组件中 文章目录 介绍简单使用多个插槽省写默认/后备内容作用域插槽常用实例Element-ui的el-table 废弃用法slot attributeslot-scope attribute 介绍 我们在定义一些组件的时候&#xff0c;由于组件内文字想要自定义&#…

python脚本记日志

1、使用RotatingFileHandler&#xff0c;可以实现日志按大小回滚&#xff08;设置&#xff0c;最多备份几个日志文件&#xff0c;每个日志文件最大值&#xff09;&#xff1a; import loggingfrom logging.handlers import RotatingFileHandler # 按文件大小回滚handlerdef ma…

ssh安全远程管理

目录 1、什么是ssh 2、ssh登陆 3、ssh文件传输 1、什么是ssh ssh是 Secure Shell 的缩写&#xff0c;是一个建立在应用层上的安全远程管理协议。ssh 是目前较为可靠的传输协议&#xff0c;专为远程登录会话和其他网络服务提供安全性。利用ssh 协议可以有效防止远程管理过程中…

蓝桥杯上岸每日N题 第五期(山)!!!

蓝桥杯上岸每日N题第五期 ❗️ ❗️ ❗️ 同步收录 &#x1f447; 蓝桥杯Java 省赛B组(初赛)填空题 大家好 我是寸铁&#x1f4aa; 冲刺蓝桥杯省一模板大全来啦 &#x1f525; 蓝桥杯4月8号就要开始了 &#x1f64f; 距离蓝桥杯省赛倒数第3天 ❗️ ❗️ ❗️ 还没背熟模…

机器学习笔记之优化算法(二)线搜索方法(方向角度)

机器学习笔记之优化算法——线搜索方法[方向角度] 引言回顾&#xff1a;线搜索方法从方向角度观察线搜索方法场景构建假设1&#xff1a;目标函数结果的单调性假设2&#xff1a;屏蔽步长 α k \alpha_k αk​对线搜索方法过程的影响假设3&#xff1a;限定向量 P k \mathcal P_k …

git 命令总结

一、本地分支和仓库里的分支不同步&#xff08;本地看不到最新的分支&#xff09; 1.使用 git fetch origin 或者git remote update origin --prune命令更新 2.git branch -r 查看 即可 二、git 合并代码 1. git 如何把分支代码合并到master 1.1 首先切换到分支 git checkou…

leetcode做题笔记48

给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 思路一&#xff1a;直接旋转 void rotate(int** matrix, int matrixSize, int…

0基础学习VR全景平台篇 第76篇:全景相机-圆周率全景相机如何直播推流

圆周率科技&#xff0c;成立于2012年&#xff0c;是中国最早投身嵌入式全景算法研发的团队之一&#xff0c;亦是全球市场占有率最大的全景算法供应商。相继推出一体化智能屏、支持一键高清全景直播的智慧全景相机--Pilot Era和Pilot One&#xff0c;为用户带来实时畅享8K的高清…

PyTorch代码实战入门

人这辈子千万不要马虎两件事 一是找对爱人、二是选对事业 因为太阳升起时要投身事业 太阳落山时要与爱人相拥 一、准备数据集 蚂蚁蜜蜂数据集 蚂蚁蜜蜂的图片&#xff0c;文件名就是数据的label 二、使用Dataset加载数据 打开pycharm&#xff0c;选择Anaconda创建的pytorch环…

[Vue3] 基础面试 (一)

文章目录 1.Vue 3相对于Vue 2有哪些主要改进2.Vue 3中如何处理组件的Props3.Vue 3中的Composition API是什么, 它与Options API有何不同4.Vue 3中的响应式数据是如何工作的5.Vue 3中的ref和reactive有何区别6.Vue 3中的watchEffect和watch有何区别 1.Vue 3相对于Vue 2有哪些主要…

医疗流程自动化盛行,RPA成为医疗保健行业的重点应用技术

随着我们进入新的科技纪元&#xff0c;机器人流程自动化&#xff08;RPA&#xff09;正快速地改变着我们的游戏规则。简单来说&#xff0c;RPA 就是模仿人类与电子系统的互动&#xff0c;自动化执行重复性的任务和操作序列。 医疗保健领域中&#xff0c;RPA 的应用具备巨大的潜…