react18以于2022年3月29日发布正式版本。
变更日志
看看怎么使用react18。
启动
react18只支持并发模式,也就是不再推荐ReactDOM.render了。
启动项目需要这样启动
ReactDOM.createRoot( document.getElementById('root')).render(<App/>)
批量更新
在Councurrent模式中,更新是以优先级为依据进行合并的。
18之前的批量更新,setTimeout或者Promise.then的更新,react无法控制,如
class BatchState1 extends React.Component {
state = {number: 0}
onclick = () => {
this.setState({number: this.state.number+1})
console.log(this.state.number);
this.setState({number: this.state.number+1})
console.log(this.state.number);
setTimeout(()=>{
this.setState({number: this.state.number+1})
console.log(this.state.number);
this.setState({number: this.state.number+1})
console.log(this.state.number);
})
}
render(){
return <div>
<p>{this.state.number}</p>
<button onClick={this.onclick}>+</button>
</div>
}
}
可以看到setTimeout中的setState无法被合并。原因就是18之前的批量更新是:伪代码
function handleClick() {
isBatchingUpdate = true
//执行click事件
isBatchingUpdate = false
}
在执行click事件的时候,批量更新按钮开启,但是setTimoeut是红任务,等到他执行的时候,isBaatchingUpdate已经关闭,所以就不是批量更新。
开启并发模式
当我么开启并发模式之后
并发模式下的批量更新,是通过更新优先级的机制实现的。如伪代码
const UpdateQueue = [];
function setState(newState) {
// 每个更新都有优先级,优先级相同的会被合并
const update = { payload: newState, priority: 0 };
UpdateQueue.push(update);
}
并发模式下,每个更新都有一个优先级的概念,相同优先级的update会被合并,这也就是为什么并发模式下打印的结果是0011。
因为,一开始的两个更新,
- 创建了两个优先级相同的Update,他们会被合并。然后setTimeout又创建了两个相同的update。他们的update也会被合并。所以就导致了最后结果是2.
第一次合并,结果是1,第二次合并,结果是2。 - 跟18以前通过开关开启的批量更新效果不一样。
Suspense
- Suspense让组件在渲染之前进行等待,并在等待的时候显示fallback的内容。
- Suspense内的组件子树比组件树的其他部分拥有更低的优先级。
- 一般用于异步加载的组件。
通过React.lazy传入一个函数返回import()的promise。lazy的实现原理是
当页面渲染到lazy包裹的组件的时候,才会去执行()=>import()去请求文件。然后才会渲染真正的组件。这就是lazy的原理。
上图我们通过React.lazy加上setTimeout延时了3s才返回了组件。效果如:
在前三s内,显示了加载中,
然后才显示了组件。
错误处理边界
异步promise组件可能报错。需要俘获。
静态方法getDerviedStateFromError的返回值会作为state。!在这里插入图片描述
SuspenseList
- SuspenseList通过编排向用户显示这些组件的顺序,来帮助协调许多可以挂起的组件。
- revealOrderi定义了SuspenseList子组件应该显示的顺序(forwards, backwards, together)
- together,在所有的子组件都准备好了再显示,而不是一个一个显示
- forwards从前往后显示
- backwards从后往前显示
- tail 指定如何i西安市SuspenseList中未加载的项目
- 默认请求库下,SuspenseList将显示列表中所有fallback
- collapsed仅显示列表中下一个fallback
- hidden未加载的项目不显示任何信息。
例子
test1一秒后返回。test2三秒,test3四秒。
而SuspenseList上有个属性。revealOrder,则用于规定这三个组件的呈现情况。
- together,在所有的子组件都准备好了再显示,而不是一个一个显示
- forwards从前往后显示
- backwards从后往前显示
还有一个tail属性,
- 默认情况下,SuspenseList将显示列表中所有fallback
- collapsed仅显示列表中下一个fallback,加载谁显示谁。
- hidden未加载的项目不显示任何信息
React新的api
StartTransition
- 接收一个回调的函数,用来告诉react需要推迟的state
- 允许组件将速度较慢的数据获取更新推迟到最后渲染,以便能够立即获取更重要的更新。
例子
- 百度的输入框。在输入第一个数字之后显示下拉框,然后输入第二个数字的时候,显示第二个下拉框。
- 当然是希望,在输入第一个数字的时候,下拉框正在渲染的时候,如果遇到了输入第二个数字,那么就需要优先去响应输入第二个数字的ui,然后再显示下拉框。
每次输入一个数字都会渲染几千行,导致第二次输入数字后卡顿。使用startTransition包裹,开启渐变更新,降低其优先级,那么输入框的交互就会先响应。效果就会好很多。
- react中,每种更新都有优先级。每次更新都会产生对应的update,高优先级的打断低优先级的任务。可以看Scheduler的原理。
useDeferredValue
- 返回一个延迟响应的值
- 在useDeferredValue内部会调用useState并触发一次更新,但是此更新的优先级很低、
- 可以看作是startTransition的语法糖。
- 这次没使用startTransition,而是使用useDeferredValue,可以看作是,延迟了keyword的更新,将keyword传给他,他返回了一个新的值。
- 但是当keyword改变的时候,在useDeferredValue也会改变deferrText,即调用一次setState改变返回的值。但是useDeferredValue的优先级非常低,导致输入框的高优先级先执行了,再去改变deferrText,从而渲染列表。
这是我理解的伪代码,源码可能不是这样的,只是为了理解useDefferValue的用法而已。
为了那么依赖keyword值的大型渲染可以晚一点执行。
useTransition
- useTransition允许组件在切换到下一个页面之前等待内容加载,从而避免不必要的加载状态。
- 他允许组件将速度较慢的数组获取更新推迟到最后渲染,以便能够立即渲染重要的更新(startTransition?)
- useTransition返回两个值,第一个startTransition是一个接收回调的函数,用来告诉react需要推迟的state。
- 第二个isPending是一个布尔值,这是 react通知我们是否正在等待过度的完成
例子
当我们多个页面切换的时候,比如用了Suspense包裹,那么可能切换的时候,fallback会被多次渲染。
如
可以很明显看到,加载中显示了两次。
此时换上useTransition hooks。
根据num加载不同的异步组件。修改num的值的时候,加上startTransition包裹。
效果:
可以很明显看到,组建的切换并没有fallback。这就是startTransition的效果。用于切换下一个页面的时候,等待内容加载完毕再切换。
那么如果页面准备i太久呢?所以react返回一个isPending来告诉你,页面是否正在准备内容。
- 这样也可以做过度效果。这个startTransition跟Ract.startTransition有区别的。
- 一般适用于,页面切换很快,才会用,少点loading的效果
…
官网https://github.com/facebook/react/blob/main/CHANGELOG.md