00 - react 18 相关

原文链接
首先现在通过 react 17.0.2 创建新项目,会直接提示需要更新 api:

在这里插入图片描述

v 17 的版本主要是是垫脚石版本,用以稳定 CM;V18 作为 React 的下一个大版本将关注点放在了并发模式上也就是谈论了很久的(Concurrent Mode);

Root API 的改变

如前面截图,通过 react 17.0.2 创建新项目,会直接提示需要更新 api;从原有的 ReactDOM.render 修改为 createRoot;
具体可以参考:https://github.com/reactwg/react-18/discussions/5

修改渲染 API:

import * as ReactDOM from 'react-dom';

function App() {
  return (
    <div>
      <h1>Hello World</h1>
    </div>
  );
}

const rootElement = document.getElementById("root");

ReactDOM.render(<App />, rootElement, () => console.log("renderered"));

修改为:

import * as ReactDOM from 'react-dom';

function App({ callback }) {
  // Callback will be called when the div is first created.
  return (
    <div ref={callback}>
      <h1>Hello World</h1>
    </div>
  );
}

const rootElement = document.getElementById("root");

const root = ReactDOM.createRoot(rootElement);
root.render(<App callback={() => console.log("renderered")} />);

ref 属性

这里简单提一下 ref 属性;​

上面的以回调的形式给 ref 传入函数,只会触发一次;​

对于 ref 属性,React 将在组件挂载时,会调用 ref 回调函数并传入 DOM 元素,当卸载时调用它并传入 null。
ref 会在 componentDidMount 或 componentDidUpdate 生命周期钩子触发前更新。


如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。

import logo from './logo.svg';
import './App.css';
import { useState } from 'react';

function App() {
  const [num, setNum] = useState(0)
  const callback= (e) => {
    console.log(e)
    console.log("renderered")
  }

  const onhandleClick = () => {
    console.log(num)
    setNum(num + 1)
  }

  return (
    <div className="App" ref={callback}>
      <header className="App-header" onClick={onhandleClick}>
        <img src={logo} className="App-logo" alt="logo" />
        <h1>{num}</h1>
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

CM 是什么

react 渲染模式

首先我们需要知道 react 渲染的几种模式:
在这里插入图片描述

具体看参考官方这篇文章:https://zh-hans.reactjs.org/docs/concurrent-mode-adoption.html

react 渲染过程

然后我们需要了解一下 react 渲染的过程:

  • render 阶段:在render阶段会计算一次更新中变化的部分(通过diff算法),因组件的render函数在该阶段调用而得名。render阶段可能是异步的(取决于触发更新的场景)。
  • commit 阶段:在commit 阶段会将 render 阶段计算的需要变化的部分渲染在视图中。对应 ReactDOM 来说会执行 appendChild、removeChild 等。commit 阶段一定是同步调用(这样用户不会看到渲染不完全的 UI)

我们通过 ReactDOM.render 创建的应用属于 legacy 模式。在该模式下一次 render 阶段对应一次 commit 阶段。

如果我们通过ReactDOM.createRoot 创建的应用属于 CM(concurrent模式)在CM下,更新有了优先级的概念,render阶段可能被高优先级的更新打断。

所以 render 阶段可能会重复多次(被打断后重新开始)。
可能多次 render 阶段对应一次 commit 阶段。

CM 模式存在很多新特性:异步可中断更新、优先级调度、批量更新等;

节点自动批量更新 (Automatic batching )

v 17 仅在事件处理函数中实现。而 Promise 链、异步代码或者原生事件处理函数的使用会打破这种行为(在这些场景中批处理会失效)。在 v 18 中,自动批处理会在原生事件处理函数、Promise 链和异步代码自动完成。

function handleClick() {
  // React 17: 重新渲染发生在所有状态都更新之后。这被称为批处理。
  // 这也是 React 18 的默认行为。
  setIsBirthday(b => !b)
  setAge(a => a + 1)
}

// 在下面代码块中,React 18 会自动批处理,但是 React 17 不会。
// 1. Promises:
function handleClick() {
  fetchSomething().then(() => {
    setIsBirthday(b => !b)
    setAge(a => a + 1)
  })
}

// 2. 异步代码:
setInterval(() => {
  setIsBirthday(b => !b)
  setAge(a => a + 1)
}, 5000)

// 3. 原生事件监听器:
element.addEventListener("click", () => {
  setIsBirthday(b => !b)
  setAge(a => a + 1)
})

如果某些关键组件不想被自动更新(比如某个状态更改后要立刻从 DOM 中获取一些内容)可以使用 ReactDOM.flushSync() 退出操作:

import { flushSync } from 'react-dom'; // Note: react-dom, not react

function handleClick() {
  flushSync(() => {
    setCounter(c => c + 1);
  });
  // React has updated the DOM by now
  flushSync(() => {
    setFlag(f => !f);
  });
  // React has updated the DOM by now
}

具体可以查看这篇官方文章;

其他:

  • 新的 Suspense fallback
  • 新的 Suspense SSR 架构

参考


https://juejin.cn/post/6974617278784471048
https://juejin.cn/post/7014683796821770247
https://juejin.cn/post/6968777636801675301
https://zh-hans.reactjs.org/docs/concurrent-mode-adoption.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jiegiser#

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值