今早来又莫名其妙的遇到了 bug,排查了一下是 useEffect 导致的。今天就再来详细的学习一下 react useEffect。
为什么要?
我们知道,react 的函数组件里面没有生命周期的,也没有 state,没有 state 可以用 useState 来替代,那么生命周期呢?
useEffect 是 react v16.8 新引入的特性。我们可以把 useEffect hook 看作是componentDidMount、componentDidUpdate、componentWillUnmounrt三个函数的组合。
详解
原来我们是这么写 class component
class LifeCycle extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidMount() {
document.title = `you clicked me ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `you clicked me ${this.state.count} times`
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={()=> this.setState({count: this.state.count + 1})}>
Click me
</button>
</div>
)
}
}
现在我们使用 hook 来写 function component
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `you clicked ${count} times`;
})
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}
Click me
</button>
</div>
)
}
上面的这段代码,每次 count 变更以后,effect都会重新执行。
我们日常开发中,获取数据、订阅以及手工更改 React 组件中的 DOM(我之前就更改过)都属于副作用。有些副作用可能需要清除,所以需要返回一个函数,比如设置定时器、清除定时器。
class Example extends Component {
constructor(props) {
super(props);
this.state = {
count: 0;
}
}
componentDidMount() {
this.id = setInterval(() => {
this.setState({count: this.state.count + 1})
}, 1000)
}
componentWillUnmount() {
clearInterval(this.id)
}
render() {
return <h1>{this.state.count}</h1>;
}
}
使用 Hook 的示例
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>
}
我们可以让 React 跳过对 effect 的调用,让 effect 的执行依赖于某个参数(传利数组作为 useEffect 的第二个可选参数就可以了)。倘若仅仅只想执行一次,那么就传递一个空的数组作为第二个参数,这个时候 useEffect hook 不依赖于 props 或者任何值,所以永远都不重复执行。
在过去的性能优化里,在某些情况下,每次渲染后都执行清理或者执行 effect 都可能会导致性能的问题。在 class 组件中,我们通过在 componentDidUpdate 添加 prevProps或者 prevState 来解决。
componentDidUpdate(prevProps, prevState) {
if(prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`
}
}
这个是非常常见的需求。而如今,倘若我们使用 function component,代码会非常的简洁。
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count])
总结
Hook 是对函数式组件的一次增强,v16.8 出现以后才出来的 hook 特性。在这之前,我看有的代码里面,通过一个<InitialCallBack>来放到函数时组件的最前面,通过在<InitialCallBack>的 componentDidMount 来获取后端接口数据。
就是变相的往 Hook 里面引入生命周期。
然而 v16.8 出来了,这个问题完全解决了。
Hook的语法更简洁易懂,消除了 class 的声明周期方法导致的重复逻辑代码。
同时,在某种程度上解决了高阶组件难以理解和使用的问题。
然而 Hook 并没有让函数时组件做到 class 组件做不到的事情,只是让很多组件变得写的更简单。class 组件不会消失,hook 化的函数时组件将是趋势。