React 18: Hooks & Components & APIs

React 的生命周期
  • 横轴代表时间的流逝,纵轴代表React需要执行的步骤(render,commit)

React Hooks

Hooks related to performance:

  • useCallback, useMemo, useTransition, useDeferredValue, useOptimistic
  • useLayoutEffect, useInsertionEffect

useId

  • Notable: 不应该用来生成 List key
  • 使用- 无障碍属性生成 unique ID
  • 使用- 为多个元素生成ID
  • 使用- 为Batch ID指定共享前缀
  • 使用- 服务端和客户端使用相同的前缀

useInsertionEffect

  • vs. renderuseLayoutEffect中注入样式
  • 直接在JS代码中编写样式,而不是编写CSS文件.
    1. 使用编译器静态提取到CSS文件
    2. 内联样式
    3. 运行时注入 style
      计算 - 浏览器频繁地重新计算样式
      慢 - useInsertionEffect可以解决

useCallback

  • useCallbackdependency 是使用 Object.is() 进行浅比较.
  • useCallback 目的是提高React 的性能,防止re-render. 一般使用存在2个场景
    1. 函数作为一个 prop 传递给子组件,子组件是一个slowRenderComponent; 该函数作为 prop传递给 React.memo 包裹的子组件
    2. 函数作为 useEffect dependency. 也可以使用 useCallback 包裹 function, 防止 re-render.
export interface IFilterState {
  [key: string]: string[] | string;
}

useDeferredValue

主要用于优化渲染性能, 避免多次更新状态和重新渲染组件
debounce的区别是: useDeferredValue 适用于渲染, debounce 使用于接口的调用.
useTransition的区别是: useDeferredValue 作用于 state, startTransition 可以包裹一段同步程序, 作用于set函数,

useLayoutEffect

useLayoutEffectbody中代码运行完,再进行repaint
scene1: 计算元素的位置.比如说 tooltip
scene2: re-direct 到别的页面(换句话说,如果没有权限, 当前页面休想看见一点)

 if (campaign_id  && ad_id) {
      location.href = `${XXX}? aadvid=${aadvid}`;
 }

useMemo

useMemo + React.memo 支持跳过子组件的 re-render/expensive calculation

useRef

  • 不要在渲染期间写入或者读取 ref.current,也就是不要将ref.current写在函数之中, 放在Event handleruseEffect
  • React 创建 dom节点并将其渲染到屏幕时,React 将会把 DOM 节点设置为 ref 对象的 current 属性
  • 避免重复创建 DOM
  • 使用 ref 引用一个值, 间隔器
export default  VideoComponent() {
  const playerRef = useRef(null);

 const  getPlayer = () => {
    if (playerRef.current !== null) {
      return playerRef.current;
    }
    const player = new VideoPlayer();
    playerRef.current = player;
    return player;
  }

useState

React 的心智模型

useSyncExternalStore

Tearing React 18之前渲染是不可以中断的,因此会出现卡顿现象,在18中增加了lane和时间分片(5ms)那渲染是可以中断的,那在用户执行输入的过程中,页面展示的状态是状态不一致.Redux 8使用useSyncExternalStore强制一致,但是当你停止操作,状态渲染出来是一致的.

import { useSyncExternalStore } from 'react';

export default function ChatIndicator() {
  const isOnline = useSyncExternalStore(subscribe, getSnapshot);
  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}

function getSnapshot() {
  return navigator.onLine;
}

function subscribe(callback) {
  window.addEventListener('online', callback);
  window.addEventListener('offline', callback);
  return () => {
    window.removeEventListener('online', callback);
    window.removeEventListener('offline', callback);
  };
}

useTransition

useTransition 是一个帮助你在不阻塞 UI 的情况下,更新状态的 React Hook

官推

const [isPending, startTransition] = useTransition()
const [tab, setTab] = useState('about');

  function handleSelectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }
  • startTransition 中需要包裹一个异步函数, setXXState(). [如果异步更新一个value,请使用 useDeferredValue]
  • 将当前选项卡更新状态放在一个过渡中,startTransition 中包裹的 state 更新允许被打断. (e.g.: 将选项卡的切换标记为transition state, 切换A后立即切换成 B, A 的渲染就会被打断)
  1. 转换效果可以被中断,在渲染期间可以和页面其他地方进行交互;也就是渲染期间点击页面,页面会给到反应
  2. 转换效果可以防止不必要的加载指示符
    Troubleshooting
  • 确保startTransition中的代码是同步运行的. setTimeout 不能放在 startTransition 中, await 也不能放在 startTransition
  • 传递给startTransition的函数会立即运行,在React运行的时候会使用transition标记

APIs

  • 一般API
    createContext, forwardRef, use
  • 性能优化相关
    React.lazy, React.memo, React.startTransition

lazy

  • lazy 能够让你在组件第一次被渲染之前,延迟加载组件的代码
  • lazy(load) load function
import { lazy } from 'react';
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));

function Editor() {
  // ...
}

startTransition

startTransition(scope)
startTransition 可以让你在不阻塞 UI 的情况下更新 state. 将state标记为Transition渲染
vs useTransition 不提供 isPending 状态来跟踪一个 transition 是否正在进行

import { startTransition } from 'react';
function TabContainer() {
  const [tab, setTab] = useState('about');

// 在回调函数中标记 transition
  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }
  // ...
}

memo

  • React 通过浅比较来比较旧的和新的props.
  • 可以指定自定义比较函数arePropsEqual, 我们必须比较每一个props, 包括函数. deepClone可能变得非常缓慢.

use

use API 可以读类Promise或者 Context 中的值. use 可以替代useContext

React Dom

  • API: ReactDOM.createRoot, ReactDOM.hydrateRoot, ReactDOM.createPortal
  • related to performance: ReactDOM.flushSync
  • <div/> 通用组件

createRoot

  • onRecoverableError 适用于组件中可能出现可恢复性错误的场景
  • an app fully built with React 使用一次 createRoot, modal 使用 createPortal
  • a page partially built with React 使用 多次
const navDomNode = document.getElementById('navigation');
const navRoot = createRoot(navDomNode); 
navRoot.render(<Navigation />);

const commentDomNode = document.getElementById('comments');
const commentRoot = createRoot(commentDomNode); 
commentRoot.render(<Comments />);
  • onRecoverableError 重试机制尝试重新加载, 注意使用ErrorBoundary
    可以恢复,就重试一次尝试恢复,不可以就展示ErrorBoundary 的后备UI
    我们可以尝试使用 try/catch 块捕获异常,并在 onRecoverableError 函数中进行自定义的错误处理逻辑
  1. 网络通信错误
  2. 图片加载错误

createPortal

利用ReactDOM.createPortal 提供一个独立于主体的 Dom
createPortal 允许你将 JSX 作为 children 渲染至 DOM 的不同部分。

  1. portal 只改变 DOM 节点的所处位置
  2. 使用createPortal 渲染model
  3. Portal 中的事件传播, 遵循 React 树而不是 DOM

  1. 利于TabList布局, inputBar 和Tab放在一起布局,将ref传递到下面的子组件
<div ref={searchRef} />
   <VideoList
        currentTab={currentTab}
        getSearchContainer={() => searchRef.current}
   />

React synthetic Event:

有些事件,如 onAbort 和 onLoad,在浏览器中不冒泡,但是在 React 中仍然会冒泡
提供了与原生事件一致的: nativeEvent
与原生 DOM 事件标准一致,也提供了而为的属性和方法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容