react hooks 是 React 16.8 的新增特性。 它可以让我们在函数组件中使用 state 、生命周期以及其他 react特性,而不仅限于 class 组件。react hooks 的出现,标示着 react中不会在存在无状态组件了,只有类组件和函数组件。具体可查看官网。
优势:
- 函数组件不能使用state,遇到交互更改状态等复杂逻辑时不能更好地支持,hooks让函数组件更靠近class组件,拥抱函数式编程。
- 解决副作⽤问题,hooks出现可以处理数据获取、订阅、定时执行任务、手动修改 ReactDOM这些⾏为副作用,进行副作用逻辑。比如useEffect。
- 更好写出有状态的逻辑重用组件。
- 让复杂逻辑简单化,比如状态管理:useReducer、useContext。
- 函数式组件比class组件简洁,开发的体验更好,效率更⾼,性能更好。
- 更容易发现无用的状态和函数。
其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
尽可能使用标准的 useEffect 以避免阻塞视觉更新。
useLayoutEffect 的使用方法和 useEffect 相同,唯一的区别就是执行时机不一样。
对比 useLayoutEffect 与 useEffect 的执行时机
import React, {
useState, useEffect, useLayoutEffect } from 'react';
import ReactDOM from 'react-dom';
function Com() {
useEffect(() => {
console.log('useEffect 执行...');
return () => {
console.log('useEffect 销毁...');
}
});
useLayoutEffect(() => {
console.log('useLayoutEffect 执行...');
return () => {
console.log('useLayoutEffect 销毁...');
}
});
return (
<div>
{
console.log('Com 渲染')}
<h2>Com1</h2>
</div>
)
}
const App = props => {
const [ count, setCount ] = useState(0)
return (
<div>
<Com />
{
count}
<button onClick={
() => setCount(count + 1)}>count + 1</button>
</div>
)
}
ReactDOM.render(<App />, root);
上面的例子中在 Com 组件中同时使用了 useLayoutEffect 和 useEffect,在页面初次渲染时可以看到控制台打印顺序为 Com 渲染 → useLayoutEffect 执行… → useEffect 执行…。
当点击 App 组件按钮更新状态导致 Com 重新渲染,打印顺序为 Com 渲染 → useLayoutEffect 销毁… → useLayoutEffect 执行… → useEffect 销毁… → useEffect 执行…。
在刚接触 React Hooks 时,说到执行时机我们一般会和类组件的生命周期去类比,下面是一个 useLayoutEffect、useEffect 与类组件生命周期配合使用的例子。
对比 useLayoutEffect、useEffect 与类组件生命周期的执行时机
import React, {
useEffect, useLayoutEffect, Component } from 'react';
import ReactDOM from 'react-dom';
// 使用 useLayoutEffect 和 useEffect 的函数组件
function Com() {
useEffect(() => {
console.log('useEffect 执行...');
return () => {
console.log('useEffect 销毁...');
}
});
useLayoutEffect(() => {
console.log('useLayoutEffect 执行...');
return () => {
console.log('useLayoutEffect 销毁...');
}
});
return (
<div>
{
console.log('Com 渲染')}
<h2>Com1</h2>
</div>
)
}
// 使用生命周期的类组件
class App extends Component {
state = {
count: 0 }
setCount = () => {
this.setState({
count: this.state.count + 1 });
}
componentDidMount() {
console.log('App componentDidMount');
}
componentDidUpdate() {
console.log('App componentDidUpdate');
}
render() {
return (
<div>
{
this.state.count}
<Com />
{
console.log('App 渲染')}
<button onClick={
this.setCount}>count + 1</button>
</div>
)
}
}
ReactDOM.render(<App />, root);
上面例子中 useLayoutEffect 和 useEffect 依然在 Com 组件中使用,App 组件为类组件,Com 作为 App 的子组件,在首次渲染时控制台的打印顺序为 App 渲染 → Com 渲染 → useLayoutEffect 执行… → App componentDidMount → useEffect 执行…。
而点击按钮更改状态触发重渲染时,打印顺序为 App 渲染 → Com 渲染 → useLayoutEffect 销毁… → useLayoutEffect 执行… → App componentDidUpdate → useEffect 销毁… → useEffect 执行…。
总结下就是useLayoutEffect 的执行时机要早于 useEffect,useLayoutEffect的执行在类组件生命周期前,useEffect 的执行在类组件生命周期后,官方的建议是要求我们尽量使用useEffect,以避免阻塞视觉更新,如果是将代码从类组件重构为 React Hooks,并且使用 useEffect出现问题,再考虑使用 useLayoutEffect,服务端渲染时使用useLayoutEffect 会触发警告。