type
status
date
slug
summary
tags
category
icon
password

其他

React使用经验

1. Props 透传

props 透传是将单个 props 从父组件向下多层传递的做法。 理想状态下,props 不应该超过两层。 当我们选择多层传递时,会导致一些性能问题,这也让 React 官方比较头疼。 props 透传会导致不必要的重新渲染。因为 React 组件总会在 props 发生变化时重新渲染,而那些不需要 props,只是提供传递作用的中间层组件都会被渲染。 除了性能问题外,props 透传会导致数据难以跟踪,对很多试图看懂代码的人来说也是一种很大的挑战。
解决这个问题的方法有很多,比如 React Context Hook,或者类似 Redux 的库。 但是使用 Redux 需要额外编写一些代码,它更适合单个状态改变很多东西的复杂场景。简单的项目选择使用 Context Hook 是更好的选择。

2. 导入代码超出实际所用的代码

React 是一个前端框架,它有着不小的代码体积。 我们在编写 React 程序时,应该避免导入很多用不到的模块。因为它们也会被打包到运行时代码发送到用户的客户端/浏览器/移动设备上。额外的依赖会导致应用的体积膨胀,增加用户的加载时间,让网页变慢,降低用户体验度。
为了保证良好的用户体验度,我们应该让 FCP 保持在 1.8 秒以内,所以我们需要简化代码体积。 现代的打包工具都有摇树功能,使用各种方式来缩小和压缩我们用于生产的代码,比如 webpack。但是在有些情况下它不能很好的去处无用的代码,我们最好知道那些代码应该被打包,而不是仅仅依靠打包工具来尝试修复我们的代码问题。 现在的 JavaScript 已经经历了多次重大更新,拥有了非常多的新功能。在过去我们需要使用 lodash 这类库来实现这些功能,但是现在 lodash 的优势在慢慢减少。

3. 关于业务逻辑和组件逻辑分离

在过去,很多人认为 React 组件应该包含逻辑,逻辑是组件的一部分。但是拿到今天来看,这个观点是有问题的。
将组件和逻辑放到一起会让组件变得复杂,当修改或者增加业务逻辑时,对开发者来说更加复杂,而且想了解整个流程也更加具有挑战性。
将组件和逻辑分离,有两个好处:
  1. 关注分离点。
  1. 重用业务逻辑。

4. 每次渲染的重复工作

即使你是经验丰富的 React 老手,可能仍然做不到对渲染这件事完全了解。 渲染是经常发生并且很多时候是出乎意料的。 这是使用 React 编写组件的核心原则之一,在编写 React 组件时应该牢记在心。 同时意味着,在渲染组件的时候会重新执行某些逻辑。

(1).缓存数据

React 提供了 useMemo 和 useCallback 两个 Hook,如果使用得当,这些 Hook 可以缓存计算结果或者函数,来减少不必要的重复渲染,最终提高性能。
上面的例子是一个项目列表的展示,其中需要通过某些条件来过滤列表,最终展示给用户。这种数据过滤在前端中是不可避免的,所以我们可以使用 useMemo 来缓存过滤数据的过程,这样只有当 items 和 filter 发生变化时它才会重新渲染。

(2).尽量避免异步使用hook

react 中多次使用 setState 会进行批量更新,如:
会被 react 合并为:
当进行异步执行 hook 后:
我们会发现,执行异步操作后,组件都更新渲染了三次 ,此时的批量更新失效了。此外异步操作也会导致一些 hook 的执行顺序变得混乱,这也是为什么 react 官方推荐在 hook 内使用异步而不是使用异步包裹 hook 的原因。

5. useEffect 使用不当

useEffect 是 React 中使用率最高的 Hooks 之一。 在 class 组件的时代,componentDidMount 是一个通用的生命周期函数,用来做一些数据请求,事件绑定等。 在 Hooks 时代,useEffect 已经取代了它。但是不正确的使用 useEffect 可能会导致最终创建多个事件绑定。 下面就是一个错误的用法。
正确的做法是:
  • useEffect 的回调函数应该返回一个函数,用来解除绑定。
  • useEffect 应该提供第二个参数,为空数组,保证只会运行一次。
    • 如果第二个参数为对象属性,则表示在该属性更新时执行。

6. useState 使用不当

useState 同样是 React 中使用率最高的两个 Hook 之一。 但是令很多人困惑的是,useState 可能并不会按照他的预期去工作。 比如一个图片压缩组件:参考React实战视频讲解:进入学习(opens new window)
应该修改为:
原因在于函数是基于当前闭包使用的状态。但是状态更新后,会触发渲染,并创建新的上下文,而不会影响之前的闭包。 所以要让程序按照预期执行,必须使用下面的语法:
此外数组变更需要重新定义一个数组去替换掉它,否则你会发现视图并没有更新,原因是数组的赋值是引用传递的,react的虚拟dom发现state里面的data没有变化,所以不更新视图,而这时可以使用一个新数组。
错误:
正确:

7. 布尔运算符的错误使用

大多数情况下我们都会使用布尔值来控制页面上某些元素的渲染,这是非常正常的事情。 除此之外还有几种其他方式来处理这种逻辑,最常用的是 && 运算符,这也完全是 JavaScript 的功能,但有时它会有一些意想不到的后果。
当我们需要展示商品数量时,如果数量为 0,那么只会展示 0,而不是商品总数:0。 原因是 JavaScript 会将 0 判断为false。 所以最好不要依赖 JavaScript 的布尔值真假比较。 正确的方式如下:

8. 到处使用三元表达式进行条件渲染

三元表达式是一个非常简洁的语法,在简短的代码中非常令人满意。所以很多人喜欢在 React 中使用三元表达式来渲染组件。 但是它的问题在于难以扩展,在最简单的三元表达式中没什么问题,可一旦多个三元表达式组合到一起,就形成了难以阅读的超大型组件。
这种代码没有功能性上的错误,但是在可读性方面做得很差。 解决它的办法有两种。 第一种是使用条件判断代替三元表达式。
如果每个分支中的组件比较复杂,我们更进一步,我们使用抽象来封装组件。
大多数情况下使用条件判断的方式就够用了。使用抽象封装组件的方式有个缺点,就是组件太过于散乱,同步逻辑比较麻烦。

9. 不定义 propTypes 或者不解构 props

React 的大多数东西和 JavaScript 几乎是一样的。React 的 props 也只是 JavaScript 中的对象,这也就意味着我们可以在对象中传递许多不同的值,而组件很难知道它们。 这样组件在使用 props 时就变得比较麻烦。 很多人喜欢这么访问 props。
在不使用 TypeScript 或者不定义 propsTypes 的情况下,我们可以随意使用 props.xxx 的方式来访问 props。 为了解决这个问题,我们可以选择使用 TypeScript 为组件的 props 声明类型。 如果你没有使用 TypeScript,那么可以使用 propTypes。 同时建议将 props 以解构的方式使用。
这样组件需要哪些 props,我们一目了然。 而且当我们试图访问 props 上面不存在的属性时,会得到警告。

10. 对大型应用的代码拆分

大型的应用意味着包含大量的组件。 这时我们应该使用代码拆分的方式将应用分成多个 js 文件,在用到哪些文件时再去加载它们。这样可以让应用的初始包体积很小,让用户启动网页的速度更快。 react-loadable 是一个专门处理这件事的第三方库,使用它我们可以很好的将组件进行拆分。

11.受控组件颗粒化

当一个页面有三个展示区域,分别做了三次请求,触发了三次setState,渲染三次页面。一旦有一个区域重新拉取数据,另外两个区域也会受到牵连,触发重新加载。
接下来我们,把每一部分抽取出来,形成独立的渲染单元,每个组件都独立数据请求到独立渲染。
这样一来,彼此的数据更新都不会相互影响。

12.规范写法,合理处置细节问题

(1).绑定事件尽量不要使用箭头函数

react更新来大部分情况自于props的改变(被动渲染),和state改变(主动渲染)。当我们给未加任何更新限定条件子组件绑定事件的时候,或者是PureComponent 纯组件, 如果我们箭头函数使用的话。
每次渲染时都会创建一个新的事件处理器,这会导致 ChildComponent 每次都会被渲染。
即便我们用箭头函数绑定给dom元素。
每次react合成事件事件的时候,也都会重新声明一个新事件。
对于dom,如果我们需要传递参数。我们可以这么写。

(2).函数式组件使用 useMemo 避免重复声明

函数式组件,数据更新就等于函数上下文的重复执行。那么函数里面的变量,方法就会重新声明。
每次点击button的时候,都会执行Index函数。handerClick1 , handerClick2handerClick3都会重新声明。为了避免这个情况的发生,我们可以用 useMemo 做缓存,我们可以改成如下。

React的重新渲染详解

关于 React 渲染

React 中的渲染和重新渲染。
  • 渲染:React 让组件根据当前的 props 和 state 描述它要展示的内容。
  • 重新渲染:React 让组件重新描述它要展示的内容。
要将组件显示到屏幕上,React 的工作主要分为两个阶段。
  • render 阶段(渲染阶段):计算组件的输出并收集所有需要应用到 DOM 上的变更。
  • commit 阶段(提交阶段):将 render 阶段计算出的变更应用到 DOM 上。

什么时候重新渲染

初始化渲染之后,下面的这些原因会让React重新渲染组件:
  1. 类组件
  • 调用 this.setState 方法。
  • 调用this.forceUpdate方法。
  1. 函数组件
  • 调用 useState 返回的 setState。
  • 调用 useReducer 返回的 dispatch。
  1. 其他
  • 组件订阅的 context value 发生变更
  • 重新调用 ReactDOM.render( <AppRoot>)
假设模块树如下:
默认情况,如果父组件重新渲染,那么 React 会重新渲染它所有的子组件。当用户点击组件 A 中的按钮,使 A 组件 count 状态值加1,将发生如下的渲染流程:
  1. React将组件A添加到重新渲染队列中。
  1. 从组件树的顶部开始遍历,快速跳过不需要更新的组件。
  1. React发生A组件需要更新,它会渲染A。A返回B和C
  1. B没有被标记为需要更新,但由于它的父组件A被渲染了,所以React会渲染B
  1. C没有被标记为需要更新,但由于它的父组件A被渲染了,所以React会渲染C,C返回D
  1. D没有标记为需要更新,但由于它的父组件C被渲染了,所以D会被渲染。
在默认渲染流程中,React 不关心子组件的 props 是否改变了,它会无条件地渲染子组件。很可能上图中大多数组件会返回与上次完全相同的结果,因此 React 不需要对DOM 做任何更改,但是,React 仍然会要求组件渲染自己并对比前后两次渲染输出的结果,这两者都需要时间。
一道面试题
问:首次渲染和按下button控制台输出的顺序是什么?
首次渲染:此时控制台打印111666
按下button后:控制台输出222444333555

forwardRef和useImperativeHandle

React.forwardRef字面意思理解为转发Ref,它会创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。其主要作用是:

1.转发refs到DOM组件

ref 不像 props 作为参数可以传递,所以要想传递 ref 得用 forwardRef。
因此,当 React 附加了 ref 属性之后,ref.current 将直接指向 DOM 元素实例。
如果一个组件被多次使用,正常情况下想要调用其组件内的方法需要传入props来调用,每次传入的话就比较多余。所以就引入了React.forwardRef。(在tsx中的运用)。
子组件暴露出来的handleShowModalhandleCancel在父组件就可以使用selectFileModalRef.current?.handleShowModal() 来调用进行使用。

2.在高阶组件中转发 refs

首先要搞清楚什么是高阶组件:高阶组件(HOC)它是一种基于 React 的组合特性而形成的设计模式。
具体而言,高阶组件是参数为组件,返回值为新组件的函数
如果你对 HOC 添加 ref,该 ref 将引用最外层的容器组件,而不是被包裹的组件。
此时需要用到forwardRef

SWR去除重复请求

概览#(opens new window)

该示例中,useSWR hook 接受一个字符串 key 和一个函数 fetcherkey 是数据的唯一标识符(通常是 API URL),并传递给 fetcherfetcher 可以是任何返回数据的异步函数,你可以使用原生的 fetch 或 Axios 之类的工具。
基于请求的状态,这个 hook 返回 2 个值:dataerror

特性#(opens new window)

仅需一行代码,你就可以简化项目中数据请求的逻辑,并立即拥有以下这些不可思议的特性:
  • 极速轻量可重用的 数据请求
  • 内置 缓存 和重复请求去除
  • 实时 体验
  • 传输和协议不可知
  • 支持 SSR / ISR / SSG
  • 支持 TypeScript
  • React Native
SWR 涵盖了性能,正确性和稳定性的各个方面,以帮你建立更好的体验:
  • 快速页面导航
  • 间隔轮询
  • 数据依赖
  • 聚焦时重新验证
  • 网络恢复时重新验证
  • 本地缓存更新 (Optimistic UI)
  • 智能错误重试
  • 分页和滚动位置恢复
  • React Suspense

React封装拖拽组件

安装依赖

使用

容器组件

拖拽组件

React实现分页打印

安装依赖

使用

page-break-before 属性的运用:某些元素设置了 page-break-before: always;会另起一页。
Docker笔记React笔记-Jotai&Mobx
Loading...
慕雨
慕雨
一个普通的干饭人🍚
公告
🎉欢迎来到慕雨的博客小站🎉
这里是我的个人学习、生活记录
--- 免责声明 ---
⚠️ 本站内容仅代表个人观点
⚠️ 本站内容仅供学习参考使用