Skip to content
目录

react随记

react更新页面分两步:render和commit。 render期间:调用组件,确定屏幕显示什么。 commit期间:将更改应用到DOM。

react基础

基础
  • 组件名必须大写
  • <>空标签成为fragment,它不会在浏览器中留下痕迹,而仅用来分组。
  • JSX在底层会被转化成纯JavaScript对象,而函数不能返回两个对象,所以需要一个额外标签进行包装。
  • JSX里的元素标签必须闭合
  • html的连字符attribute要换成驼峰式。
  • class作为react的保留字,在html里需要将其换成classname。
  • 在JSX里写js逻辑或者引用变量时,加上{}。一般用在标签内和标签的attribute赋值的右操作数。
  • react是单向数据流:只能从父组件传props给子组件。
  • 组件可以使用{...props}的形式充当attibute,它会解构出来对应的attribute。这样比较简洁。
  • 条件渲染一般使用if&&? :
  • 渲染列表时记得加key,key可以来自数据库或者本地,本地的话直接用crypto.randomUUID(),或者使用uuid库
  • 组件一般是纯函数,非纯函数可能会产生副作用。副作用一般放在事件函数里,因为组件渲染时不会运行事件函数,所以事件函数可以not need to be pure。
  • 命令式编程(imperative):注重过程,写出每一步。比如操作DOM。
  • 声明式编程(declarative):注重结果,过程被封装。react就是这种,只需要改state页面就会改变,根本不需要去操作DOM。

事件

事件
  • 事件会冒泡:沿着父组件传播。除了onScroll。冒泡:bubble,传播:progation。
  • 防止事件冒泡:e.stopPropagation()
  • 每个事件都会经历三个阶段,这里以onClick事件为例:
    1. 向下传播,调用所有 onClickCapture 处理程序。一般在事件名后加Capture是为了分析子组件的事件,这个不受stopProgation的影响,因为它先被调用。
    2. 它运行被点击元素的 onClick 处理程序。
    3. 向上传播,调用所有 onClick 处理程序。
  • 浏览器有一些事件的默认行为,比如form的submit事件,它会默认重载页面。
  • 防止事件的默认行为:e.preventDefault()
  • 事件适合用来放副作用,副作用比如:改变input的值,改变显示的列表。
  • state:组件的内存,用来存值
  • 组件每次渲染相当于函数重新执行一遍,useState会返回它所记住的值给你,和值的setter,setter会触发组件重新渲染。

state

组件的内存,用来存值
  • useState会返回一个值给你,和值的setter,setter会触发组件重新渲染。
  • 组件每次渲染相当于函数重新执行一遍,下一次render时,useState会返回它所记住的值。
  • 钩子函数必须在顶层调用,类似于import。
  • 第一次执行useState时,会创建两个空数组,state和setters。第一次执行组件函数,每执行一次useState,将对应初始值push进state数组,将一个setter函数push进setters数组。后续渲染时,每个state的值都从state数组里顺次读取,每个setter从setters数组里读取。每一个setter都有对其在setters数组里的索引(cursor)的引用,因此当setter被调用时,会根据这个索引改变state数组里相应索引的值。
  • state像一个快照,组件返回的也可以理解为一个可交互的快照。所有使用state值的程序不管是setTimeout还是连续调用同一个setter还是事件,它获取的都是当时快照的值,也就是用户与页面交互时的state。
  • state更新是批处理的,它会将每个setter先加入一个待处理队列,等到render的时候再进行处理,处理完后再按照组件里的代码执行。
  • setter可以接收一个updater function作为参数,updater function的参数是state数组里对应的state,返回值是要更新的值。因为这次setter的参数是一个函数,所以用于计算的state不是组件里的快照state,而是组件维护的state数组里的对应位置的state。updater function同样会被加入队列,等待处理。setState(value)是直接将value替换掉之前的值,将value放入state数组的对应位置,setState(updater)是使用state数组里对应的值来进行计算,返回值再填入state数组的对应位置。
  • updater function的参数命名约定:使用相应state的首字母,或者全称,或者在全称前加前缀prev并使用驼峰写法。
  • 一个对象类型的state的修改也不能直接修改,也只能通过setter传一个新的对象。
  • 使用immer库就可以直接修改了。npm install immer use-immer
  • 如果一个state是数组,那修改方式也只能用setter,什么push()、pop()方法以及直接修改元素都不行,这些涉及到修改原数组,但可以用不修改原数组的方法,比如:concat()、[...arr]、filter()、slice()、map()等。sort()和reverse()会修改原数组。
  • 构建state的准则:合并关系密切的state,避免多余的state,避免深层嵌套的state,避免state的名称混淆。避免使用prop来当作state的初始值,因为prop改变后,state不会随之改变,state后续只跟setter改变。
  • 组件间共享state的值:状态提升,将state提升到同一个就近父组件,使用prop的方式传值。

render and commit

组件显示过程的步骤
  • 分三步
    1. trigger a render:初始化render、state更新。
    2. rendering the component
    3. committing to the DOM:将计算结果提交给DOM。
  • 初始化render就是main.js里的createRoot(document.getElementById('root')).render(xxx)
  • 所谓render,就是调用组件的过程。初始化的render是调用根组件,创建节点(appendChild()),后续的render是根据每个组件的state更新来的。父组件的render会带动子组件的render。不过一个子组件超多的父组件渲染时,性能可能不是很好。

保留与重置state

保留与重置state

对于JSX,react会对他建模成UI Tree,然后再渲染成DOM。其中每个react组件的state保存在这个组件所在UI Tree的位置(而不是这个组件里),相同组件(不是同一个)在UI Tree的同一位置的交换不会重置state,不同组件在UI Tree的同一位置交换会重置state。

不过,也有需要在同一位置相同组件的交换重置state的情况,有两种解决办法:
一是两个相同组件渲染在不同位置,所谓不同位置,一般用一对花括号表示一个位置,将两个组件放在两个花括号里就表示不同位置了。
二是使用不同的key,这种更好。

如果移除了组件但是想要保留它的state,有三种方法:
一是用css隐藏元素,这种方法适合简单的UI。
二是把state交给父组件,这个更通用。
三是其他来源,比如localStorage,这个看情况用。

reducer

提取所有state为一个reducer函数

统合state更新逻辑

将useState迁移到useReducer分三步:
1.将setState类型的函数改成dispatch函数,dispatch接收一个对象类型的参数,叫做action,action里的键值对是之前setState的参数,加上一个type字段,用来表示此次操作是什么操作。
2.写一个reducer函数,放在组件外或者新建一个文件,它接收两个参数,当前state和action,它返回next state。
3.引入useRreducer函数代替useState。

注意:
reducer必须是纯函数,它不能改变state,只能重新返回一个state,但是immerReducer可以。安装:npm install immer use-immer https://github.com/immerjs/use-immer

context深层组件传值

Context:父组件向子子...子组件传送数据

为了方便,替代props

使用Context的三个步骤:
1.新建一个js文件,创建一个anyContext。
2.在子组件里使用它,先引入useContext,再引入刚刚创建的context。
用法const val = useContext(anyContext)
注意:只能在组件上方立即调用。
3.从父组件提供数据给context。引入你创建的anyContext,在父组件中,将children包裹进anyContext.Provider组件,提供的值作为props传给这个组件的value。如果想要覆盖父组件的context,那就在用anyContext.Provider包装以下,传一个新值。当然,不同的context不会彼此影响。
注意:传参不复杂的话最好用props,因为props层级清晰。

组合reducer和context

组合reducer和context

适用于组件非常多的情况,比如上百个

三步
1.在一个js文件里创建两个context,父组件里创建reducer。一个context存state,一个context存disptach。
2.将state和dispatch放入context,像下面这个。
3.使用context,删除所有props。
4.在这个js文件里组合context和reducer,导出一个组件,叫xxxProvider。

好处:可以在任意子组件里调用dispatch了。

注意:xxxProvider只对子组件有效。
javascript
import { TasksContext, TasksDispatchContext } from './TasksContext.js';

export default function TaskApp() {
  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
  // ...
  return (
    <TasksContext.Provider value={tasks}>
      <TasksDispatchContext.Provider value={dispatch}>
        ...
      </TasksDispatchContext.Provider>
    </TasksContext.Provider>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13

ref

ref:不会触发render的state,react不会track它

一个普通对象

ref可以读写,它的变化不会使组件re-renders,它的值被react保存。
使用:通过useRef来创建ref

什么情况需要用到
1.存储计时器的timeoutId
2.存储DOM元素
3.存储不需要计算的JSX元素。

ref与state的不同

1.useRef返回一个{ current: initialValue },useState返回[value, setValue]
2.ref的值改变不会触发re-render,state会。
3.xxxRef.current可以直接修改,state只能通过useState修改。
4.render时不能读写xxxRef.current,state可以在任何时候读。
总的来说,就是想存一个值,但是不会影响渲染逻辑的时候,就选ref。

怎么用好ref
1.对接外部系统或浏览器API时很有用。
1.渲染时不要读写ref.current,最好放在函数里。

使用ref操作DOM

使用ref操作DOM

用于聚焦、滚动、测量大小位置等

使用
1.用null作为初始值创建一个xxxRef。
2.将xxxRef赋给DOM节点的ref attribute,此时react会将此节点的引用赋给xxxRef.current。
3.然后就可以用了,比如xxxRef.current.focus()。

当一个列表的所有元素都需要传入ref时,只创建一个ref,然后使用ref回调,也就是传一个回调函数给ref attribute。这个回调函数会在渲染时调用。要做的是在这个回调函数中将元素给这个ref,作为它的一部分。ref回调的参数是当前元素。

访问另一个组件的DOM节点

注意:函数式组件不能传ref attribute,因为react默认不让一个组件访问另一个组件的DOM元素。解决办法是使用forwardRef。
对ref.current的更改在commit期间,它会先将ref.current置为null,待更新完DOM后,再将ref.current设置为正确的值。
jsx
// 错误典范:一开始ref.current是null,是不会有play方法的
function VideoPlayer({ src, isPlaying }) {
  const ref = useRef(null);

  if (isPlaying) {
    ref.current.play();  // Calling these while rendering isn't allowed.
  } else {
    ref.current.pause(); // Also, this crashes.
  }

  return <video ref={ref} src={src} loop playsInline />;
}
1
2
3
4
5
6
7
8
9
10
11
12

Effects

使用Effects与外部系统保持同步

Effects一般指render引起的side effect,俗称逃生口,就是与react组件本体联系不大,与组件本体无关的就从这逃生口出入,它运行在commit阶段的末尾,DOM更新之后,这时可以将react组件与网络或第三方库进行同步

side effect:副作用(不含贬义),指调用函数时,产生的对函数外的变化。
pure function:纯函数,指一个函数不涉及对外操作,并且返回值与参数一一对应(same input, same output)。它不会引起side effect。如果一个函数返回随机数,那它不是纯函数,但也不会产生side effect。
react要求是组件纯函数,但组件里的事件函数没有这个要求,因为副作用基本都是从事件函数里产生的,而且,事件函数不会在渲染期间运行,不会对react组件的返回值产生影响。

这里的同步,应该指的是正常的预期的情况,不要出现异常情况。

一般effect运行时,你都可以拿到最新的部分,包括state和DOM。

使用
1.引入useEffect,useEffect第一个参数接收一个回调函数。它放在组件顶层。它在组件每次渲染后执行回调函数里的内容。
2.给Effect指定依赖,防止每次渲染都调用effect。给useEffect的第二个参数一个数组,将Effect执行所依赖的state变量作为这个数组的元素。如果这些state没有变化,那么Effect就不执行。元素是ref、prop也行。如果为空数组且useEffect的回调里没有依赖state和prop,那么这个effect在组件页面出现后(mount:页面出现时)只会运行一次,如果有依赖而空数组,那么会报错。没有第二个参数的话那么每次渲染后都会运行。
3.(可选)为了清除连接远程等操作,需要返回一个cleanup函数。cleanup函数会在每次Effect重新运行之前(重新运行,不是第一次运行)以及最终组件unmount的时候。这个操作一般在这些开发情况下使用:弹窗(需关闭弹窗,防止弹两次报错),绑定事件(移除事件监听,防止绑定两次),触发动画(清除动画,防止执行两次动画),fetching data(请求两次,但防止对获得的数据进行两次处理),

不要在useEffect的回调函数里mutate state。
如果没有外部系统,最好不要用useEffect。
如果是个使用外部系统的事件,那还是放在事件函数里比较好。
jsx
// 示例:这里的外部系统指的是浏览器的media api
import { useState, useRef, useEffect } from 'react';

function VideoPlayer({ src, isPlaying }) {
  const ref = useRef(null);

  useEffect(() => {
    if (isPlaying) {
      ref.current.play();
    } else {
      ref.current.pause();
    }
  });

  return <video ref={ref} src={src} loop playsInline />;
}

export default function App() {
  const [isPlaying, setIsPlaying] = useState(false);
  return (
    <>
      <button onClick={() => setIsPlaying(!isPlaying)}>
        {isPlaying ? 'Pause' : 'Play'}
      </button>
      <VideoPlayer
        isPlaying={isPlaying}
        src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
      />
    </>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

不需要使用Effect的情况

你可能不需要effect

1.不要使用Effect来修改state。state发生变化时,react会调用组件函数去计算页面,也就是render,然后commit到DOM,更新页面,然后运行Effect。所以改变任何数据的操作放在组件顶层。

2.不要使用Effect处理事件。

3.对于复杂的占时间的计算可以使用useMemo来缓存它的值,在其不关联state或prop更新时不会运行,示例如下。(这种一般用于创建上千个对象或者循环千遍才用)

4.state随prop变化时清空的情况不要使用Effect,因为会render两次。解决方法是创建一个组件,把这个prop当作key,对应的state也放这个组件里,key改变时,组件会重置。

5.state随prop变化时不要使用Effect,可以创建一个新state,初始值为因变量prop,直接用if语句来实现,判断这个新state与prop是否相等,相等则不改变state。不过,任何state都要保持最简单的,比如用state保存数组,不如用state保存数组元素的id。组件里最理想的形式是一路const xxx = yyy下来,if也不用。

6.重复的事件逻辑也不要用Effect。因为事件只能有一种触发方式,不需要Effect来间接触发。

7.网络请求看触发方式,如果是初始化时(mount)触发,那么使用Effect,如果是事件,那么放在事件函数里。

8.最好不要使用Effect链。

9.只执行一次的数据建议放在组件外或者限制执行次数。

10.将数据传到父组件也不要用。

11.订阅外部数据时除了用Effect之外,还可以用useSyncExternalStore钩子。

12.不由事件触发的fetch一般用Effect来写,添加好依赖项就行了,即使它可能会渲染两次。比如输入,不过这要写一个cleanup函数,以免输入过快时发生race condition。也可以将请求逻辑写到一个自定义钩子函数里。
jsx
// useMemo 缓存visibleTodos,而不运行getFilteredTodos函数,除非todos或filter改变
import { useMemo, useState } from 'react';

function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');
  const visibleTodos = useMemo(() => {
    // ✅ Does not re-run unless todos or filter change
    return getFilteredTodos(todos, filter);
  }, [todos, filter]);
  // ...
}
1
2
3
4
5
6
7
8
9
10
11

响应式Effects的生命周期

Lifecycle of Reactive Effects

Effect只做两件事:开始同步和停止同步。cleanup函数就是停止同步。

组件的生命周期
mount:组件挂载时
update:组件更新时
unmount:组件卸载时
mount --> update --> ... --> update --> unmount.

re-synchronize Effect
当组件更新时,Effect会重新同步:停止同步,开始新的同步。render --> commit --> effect start synchronizing --> render --> commit --> effect stop synchronizing --> effect start synchronizing.

Effect生命周期(每个Effect有独立的生命周期)
start synchronizing --> stop synchronizing.
写Effect时,关注一次Effect的生命周期,怎么开始同步,怎么结束同步,就好。

react在开发阶段的mount时会强制再次执行Effect来验证Effect是否可以re-synchronize,也就是验证你的cleanup函数是否可以很好地运行。就像是开门和关门来验证你的门是否正常。

react怎么知道它什么时候要去re-sunchronize:每次组件re-render时,检查依赖项变化。

每个Effect表示一个独立的同步进程,即使两个Effect依赖相同,如果不止一个,那就分开。

一般state和prop是响应式的值,基于它们计算得到的值也是,也就是组件内的所有变量都是,响应式的值变化会更新组件。依赖为空时,也就是不接受响应式的值,这个Effect只会在挂载和卸载时执行同步和断开同步。当然开发阶段会执行再执行一次。

Effect会检查使用到的变量是否都在依赖数组里声明了,没有就会报错。

依赖项避免使用对象和函数。

从Effect里分离出事件

Separating Events from Effects

Effect和event的区别
特定交互时使用event handler
非交互的同步外部操作使用Effect。
event更像是手动执行,Effect更像是自动执行。

对于响应性--reactive value
组件内声明的变量是reactive,包括state、prop、function等。
因为event是被手动触发才执行的,所以它不具有reactive。
而Effect会根据reactive value的改变而改变,所以Effect的逻辑是reactive。
如果点击事件改变了某个值,针对这个新值去做一系列处理呢?是否既可以使用event,也可以使用Effect。

对于非响应性逻辑,需要把它提取出Effect,使用useEffectEvent(an experimental API),useEffectEvent接收一个回调函数,它会返回一个Effect Event函数,Effect Event在Effect内调用,行为像一个event。这样就把非关联的响应性内容提取出去了。这种情况一般在Effect内触发事件,且至少有两个reactive value,但是这两个reactive value的Effect行为并不统一,且其中一个reactive value与事件函数有关,所以必须分离,如果事件函数是普通函数,那么需要把函数名写入依赖项,而Effect Event函数不用写进依赖项。

Effect Event的限制
只能在Effect里使用
别把它当作参数传给其他组件或者钩子

移除Effect依赖

removing Effect dependencies

除了剔除不需要的,可以将依赖值放到组件外。
Effect尽量简洁,能不放如Effect的代码就尽量不要放入。

自定义Hooks

Reusing Logic with Custom Hooks

用处
请求数据、连接、判断等等。主要是复用、重视意图而不是逻辑。

hook:钩子函数,可以使用react特性的独立函数。组件会钩入这些不同功能的函数。
可以将可重用代码放入自定义钩子
函数名以use开头,之后接大写开头的名称,组件名开头大写。
组件必须返回什么,钩子函数可以返回任意值,不返回也行。
hook将state的逻辑写进来,一般是基于state返回计算后的结果。
hook应该是pure function。将hook视为组件的一部分,这样可以考虑hook里的Effect。
钩子可以传state、function作为参数,不过如果钩子里有Effect,不要把function作为依赖,用useEffectEvent包装一下。

什么时候用?
有重复代码时,重复的不多可以不用
有Effect时,最好先写在组件里,再提取到钩子里,这样是为了发现错误。

关于钩子的命名
尽量精确描述意图,使用术语
避免涉及lifecycle之类的术语,比如useMount

react内置hooks

useCallback()

const cachedFn = useCallback(fn, dependencies)

作用:缓存函数,用来优化性能,一般用于复杂的情形。少用。

参数1:任何函数
参数2:参数1所需要的依赖项。如果不传,那和不用useCallback没区别,至少传个[]。

原理:初次render时,`useCallback`会返回`fn`。后续渲染时,如果依赖项的值没变,那么还是返回和上一次一样的函数,如果变了,会返回这次渲染过程中你传入的函数。

用途1:跳过组件的更新
  • 跳过组件的更新:使用memo包裹组件,这样,如果这个组件的props没有一个变化,那它就不会更新。
  • js每次声明同一个函数时,都创建了不同的函数,react组件内的函数就是这样。如果将函数作为prop传递给一个组件,那么函数每次render都会变化,memo就不起作用。
  • 如果将这个函数用useCallback来缓存的话,那这个函数就只有依赖项变化它才会变化,依赖项不变,函数就不变,组件也就因为memo的关系,不会更新,节省性能。而依赖项变化,那就返回一个新的函数。
用途2:当有函数作为Effect的依赖项时
  • 将这个函数用useCallback缓存,不然Effect会因为这个函数疯狂调用
  • 如果将这个函数直接在Effect里声明,这样更好,都不用useCallback了,也就不用函数作为Effect的依赖了
用途3:如果你正在编写自定义 Hook,建议将其返回的任何函数包装到 useCallback 中

useContext()

const value = useContext(SomeContext)

作用:传值到深层组件

参数:使用CreateContext创建的context。CreateContext接收一个初始值,返回一个context,context用来给子孙组件提供值。

返回:一堆值。初始值是CreateContext的参数,后续的值由SomeContext.Provider组件的value attribute提供,SomeContext.Provider存在于子组件和父组件之间。如果返回值改变,那么组件就渲染。

注意:一般useContext是用在子组件的,不能用在父组件。

useDebugValue()

useDebugValue(value, format?)

作用:用于自定义Hook,在React DevTools展示可读的调试值。

参数1:你想展示的值。
参数2:以参数1为参数的format函数,返回的值用于替代参数1来显示。

useDeferredValue()

const deferredValue = useDeferredValue(value)

作用:延迟更新一部分UI。要求你使用suspense。

参数:你想延迟的值,比如state。

返回:初始化渲染时,返回与参数相同的值。后续渲染时,先使用旧值渲染,再使用新值再来一遍渲染,只不过这次渲染可被打断,被打断后也就不能触发Effect,直到这次渲染commit完成。

用途1:打字过快时

useEffect()

useEffect(setup, dependencies?)

作用:与外部系统同步。详情见上方的关于Effect的内容。

useId()

const id = useId()

作用:生成唯一id。

参数:没有

返回:一个字符串。

用途1:赋值给元素的可访问性attributes。

注意:useId不能用来当作列表的key。

useImperativeHandle()

useImperativeHandle(ref, createHandle, dependencies?)

作用:

参数1:从forwardRef render function传回来的第二个参数ref。
参数2:一个不带参数的函数,返回一个ref handle。
参数3:依赖项。

返回:undefined。

描述:参考。一般情况下,子组件不会通过ref暴露它的DOM节点,除非使用forwardRef包裹子组件,这样在子组件的第二个参数就能接收到ref,再把ref赋给html元素的ref。这样父组件就能获取到节点。但是用了useImperativeHandle后,它的第二个参数就会返回一个自定义的ref给父组件,比如只返回节点的某个方法,比如input的focus方法,返回值一般是对象形式。除此之外,也可以获取你自定义的方法。

useLayoutEffect()

useLayoutEffect(setup, dependencies?)

作用:在浏览器绘制屏幕之前调用,可能会影响性能。尽量用useEffec()。

参数:同useEffect()。

返回:undefined。

用途1:测量布局的大小,比如元素的宽高。这样就能获取元素最新的大小。

useMemo()

const cachedValue = useMemo(calculateValue, dependencies)

作用:缓存值。

参数1:用于计算的函数,返回计算后的值。
参数2:依赖项。变化时,参数1重新执行。

返回:缓存的值。

用途1:计算量太大。
用途2:结合memo,跳过组件重渲染。
用途3:对于依赖prop的字面量赋值,也可以用。
用途4:缓存函数,函数作为prop传给子组件时,结合子组件的memo,可以防止子组件重渲染,和useCallback类似,不过useMemo是返回参数1的返回值,而useCallback是直接返回参数1。

useReducer()

const [state, dispatch] = useReducer(reducer, initialArg, init?)

作用:组合state的逻辑。

参数1:reducer函数,用来组合所有state被修改时的逻辑。
参数2:state初始值。
参数3:处理参数2后返回初始值。

返回值1:当前state。
返回值2:dispatch函数,用来调用reducer函数,处理对应的state。

用途:详情见上面的reducer部分。

useRef()

const ref = useRef(initialValue)

作用:保存值,值得改变不会触发渲染。

参数1:初始值。

返回值1:一个有current属性得对象,初始值就存在current里。

用途1:存DOM节点。

useState()

const [state, setState] = useState(initialState);

作用:就不多解释了。

useSyncExternalStore()

const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)

作用:订阅外部数据。当外部数据变化时,会重渲染组件。

参数1:一个订阅函数,返回一个取消订阅函数。作用是返回快照前的前提步骤。每当store更改时调用参数1。
参数2:一个返回快照的函数。
参数3:返回服务器渲染期间使用的快照的函数。

返回值:从参数2那里获得得快照。

用途:获取第三方状态管理库的数据。

useTransition()

const [isPending, startTransition] = useTransition()

不甚明白,后续了解

返回值1:告诉你是否有待过渡的地方。
返回值2:让你标记一个state,使这个state的更新过渡。startTransition接收一个回调函数,这个回调函数里可以调用一些setter。

react内置组件

Fragment

<></>,不过如果要在它上加key,那就用<Fragment></Fragment>

Profiler

包裹住组件用于测试这些组件的渲染性能,<Profiler>有一个onRender的attribute,它接收一个回调函数。详情就不看了。

StrictMode

方便在开发阶段发现bug。

Suspense

jsx
// SomeComponent没加载完时,使用<Loading />
<Suspense fallback={<Loading />}>
  <SomeComponent />
</Suspense>
1
2
3
4
jsx
<Suspense fallback={<BigSpinner />}>
  <Biography artistId={artist.id} />
  <Suspense fallback={<AlbumsGlimmer />}>
    <Panel>
      <Albums artistId={artist.id} />
    </Panel>
  </Suspense>
</Suspense>
1
2
3
4
5
6
7
8

react内置api

createContext:创建context。

forwardRef:使组件能暴露DOM。

lazy:延迟加载组件

jsx
import { lazy } from 'react';

const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));

<Suspense fallback={<Loading />}>
  <h2>Preview</h2>
  <MarkdownPreview />
</Suspense>
1
2
3
4
5
6
7
8

memo:跳过组件的重渲染,除非prop变化。

startTransition:平滑更新state。

jsx
import { startTransition } from 'react';

function TabContainer() {
  const [tab, setTab] = useState('about');

  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }
  // ...
}
1
2
3
4
5
6
7
8
9
10
11
12

react-dom组件

所有的html元素可以包含react的属性。

react提供的属性有:children、dangerouslySetInnerHTML、ref、suppressContentEditableWarning、suppressHydrationWarning、style。

react修改的属性有:accessKey、aria-、autoCapitalize、className、contentEditable、data-、dir、draggable、enterKeyHint、htmlFor、hidden、id、is、inputMode、itemProp、

Released under the MIT License.