一、简介
升级
- 新项目: 直接用 npm 或者 yarn 安装最新版依赖即可(如果是js,可以不需要安装types类型声明文件)
npm i react react-dom --save
npm i @types/react @types/react-dom -D
- 旧项目: 先把依赖中的版本号改成最新,然后删掉 node_modules 文件夹,重新安装:
npm i
Note
react 18已经放弃了对 ie11 的支持,将于 2022年6月15日 停止支持 ie,如需兼容,需要回退到 React 17 版本。
React 18 中引入的新特性是使用现代浏览器的特性构建的,在IE中无法充分polyfill,比如micro-tasks
二、新特性
1.createRoot
1.1、为了更好的管理root节点,支持 new concurrent renderer(并发模式的渲染),它允许你进入concurrent mode(并发模式)。
// React 17
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const root = document.getElementById('root')!;
ReactDOM.render(<App />, root);
// React 18
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = document.getElementById('root')!;
ReactDOM.createRoot(root).render(<App />);
1.2 React 18 还从 render 方法中删除了回调函数
如果需要在 render 方法中使用回调函数,我们可以在组件中通过 useEffect 实现:
// React 17
const root = document.getElementById('root')!;
ReactDOM.render(<App />, root, () => {
console.log('渲染完成');
});
// React 18
const AppWithCallback: React.FC = () => {
useEffect(() => {
console.log('渲染完成');
}, []);
return <App />;
};
const root = document.getElementById('root')!;
ReactDOM.createRoot(root).render(<AppWithCallback />);
1.3更新 TypeScript 类型定义
在定义props类型时,如果需要获取子组件children,那么你需要显式的定义它,例如这样:
// React 17
interface MyButtonProps {
color: string;
}
const MyButton: React.FC<MyButtonProps> = ({ children }) => {
// 在 React 17 的 FC 中,默认携带了 children 属性
return <div>{children}</div>;
};
export default MyButton;
// React 18
interface MyButtonProps {
color: string;
children?: React.ReactNode;
}
const MyButton: React.FC<MyButtonProps> = ({ children }) => {
// 在 React 18 的 FC 中,不存在 children 属性,需要手动申明
return <div>{children}</div>;
};
export default MyButton;
2.setState 自动批处理
批处理是指为了获得更好的性能,在数据层,将多个状态更新批量处理,合并成一次更新(在视图层,将多个渲染合并成一次渲染)
在React 18 之前,我们只在 React 事件处理函数 中进行批处理更新。promise、setTimeout、原生事件处理函数中、或任何其它事件内的更新都不会进行批处理
2.1、React 事件处理函数
import React, { useState } from 'react';
// React 18 之前
const App: React.FC = () => {
console.log('App组件渲染了!');
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
return (
<button
onClick={() => {
setCount1(count => count + 1);
setCount2(count => count + 1);
// 在React事件中被批处理
}}
>
{`count1 is ${count1}, count2 is ${count2}`}
</button>
);
};
export default App;
2.2setTimeout
import React, { useState } from 'react';
// React 18 之前
const App: React.FC = () => {
console.log('App组件渲染了!');
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
return (
<div
onClick={() => {
setTimeout(() => {
setCount1(count => count + 1);
setCount2(count => count + 1);
});
// 在 setTimeout 中不会进行批处理
}}
>
<div>count1: {count1}</div>
<div>count2: {count2}</div>
</div>
);
};
export default App;
2.3原生js事件
import React, { useEffect, useState } from 'react';
// React 18 之前
const App: React.FC = () => {
console.log('App组件渲染了!');
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
useEffect(() => {
document.body.addEventListener('click', () => {
setCount1(count => count + 1);
setCount2(count => count + 1);
});
// 在原生js事件中不会进行批处理
}, []);
return (
<>
<div>count1: {count1}</div>
<div>count2: {count2}</div>
</>
);
};
export default App;
总结:
- 在 18 之前,只有在react事件处理函数中,才会自动执行批处理,其它情况会多次更新
- 在 18 之后,任何情况都会自动执行批处理,多次更新始终合并为一次
3.flushSync
如果你想退出批量更新,你可以使用 flushSync
import React, { useState } from 'react';
import { flushSync } from 'react-dom';
const App: React.FC = () => {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
return (
<div
onClick={() => {
flushSync(() => {
setCount1(count => count + 1);
});
// 第一次更新
flushSync(() => {
setCount2(count => count + 1);
});
// 第二次更新
}}
>
<div>count1: {count1}</div>
<div>count2: {count2}</div>
</div>
);
};
export default App;
4.Suspense
Suspense 使得组件可以“等待”某些操作结束后,再进行渲染
Suspense 不再需要 fallback 来捕获
在 React 18 的 Suspense 组件中,官方对空的fallback 属性的处理方式做了改变:不再跳过 缺失值 或 值为null 的 fallback 的 Suspense 边界。相反,会捕获边界并且向外层查找,如果查找不到,将会把 fallback 呈现为 null
以前,如果你的 Suspense 组件没有提供 fallback 属性,React 就会悄悄跳过它,继续向上搜索下一个边界
// React 17
const App = () => {
return (
<Suspense fallback={<Loading />}> // <--- 这个边界被使用,显示 Loading 组件
<Suspense> // <--- 这个边界被跳过,没有 fallback 属性
<Page />
</Suspense>
</Suspense>
);
};
export default App;
现在,React将使用当前组件的 Suspense 作为边界,即使当前组件的 Suspense 的值为 null 或 undefined
// React 18
const App = () => {
return (
<Suspense fallback={<Loading />}> // <--- 不使用
<Suspense> // <--- 这个边界被使用,将 fallback 渲染为 null
<Page />
</Suspense>
</Suspense>
);
};
export default App;
如果忘记提供 fallback 属性,也不会有什么问题
三、concurrent Mode(并发模式)
并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行
js是单线程语言,同一时间只能执行一件事情。这样就会导致一个问题,如果有一个耗时任务占据了线程,那么后续的执行内容都会被阻塞
并发模式通过使渲染可中断来修复阻塞渲染限制。
Concurrent模式是React 的新功能,可帮助应用保持响应,并根据用户的设备性能和网速进行适当的调整
在 Concurrent 模式中,React 可以同时更新多个状态。
总结一句话就是:
从同步不可中断更新变成了异步可中断更新。
我们只需要把 render 升级成 createRoot(root).render() 就可以开启并发模式了
开启并发模式就是开启了并发更新么?
并发特性指开启并发模式后才能使用的特性,比如:
- useDeferredValue
- useTransition
transition用于区分 urgent 和 non-urgent updates
- Urgent updates 紧急更新,指直接交互,通常指的用户交互。如点击、输入等。这种更新一旦不及时,用户就会觉得哪里不对。
- Transition updates 过渡更新,如UI从一个视图向另一个视图的更新。通常这种更新用户并不着急看到
1.startTransition
startTransition可以用在任何你想更新的时候。但是从实际来说,以下是两种典型适用场景:
- 渲染慢:如果你有很多没那么着急的内容要渲染更新。
- 网络慢:如果你的更新需要花较多时间从服务端获取。这个时候也可以结合Suspense。
2.useTransition
在使用startTransition更新状态的时候,用户可能想要知道transition的实时情况,这个时候可以使用useTransition。
import { useTransition } from 'react';
const [isPending, startTransition] = useTransition();
如果transition未完成,isPending值为true,否则为false。
3.suspense 与transition 结合
4.useDeferredValue
使得我们可以延迟更新某个不那么重要的部分
四、新的api
1.userId
服务端渲染时,会用到
2.useSyncExternalStore
一般是三方状态管理库使用,我们在日常业务中不需要关注
3.useInsertionEffect
只建议 css-in-js 库来使用,它的执行时机在 DOM 生成之后,useLayoutEffect 之前
const useCSS = rule => {
useInsertionEffect(() => {
if (!isInserted.has(rule)) {
isInserted.add(rule);
document.head.appendChild(getStyleForRule(rule));
}
});
return rule;
};
const App: React.FC<T> = () => {
const className = useCSS(rule);
return <div className={className} />;
};
export default App;
总结:
1.@types/react 18.0.40报错 Property ‘children’ does not exist on type ‘IntrinsicAttributes & CollapseProps’