跳到主要内容

用 useEffect 模拟组件生命周期

useEffect

让函数组件模拟生命周期

  • 默认函数组件没有生命周期
  • 函数组件是一个纯函数,执行完后即销毁,自己无法实现生命周期
  • 使用 Effect Hook 把生命周期 “钩” 到纯函数中

例子

import React, {useState, useEffect} from "react";

function LifeCycles() {
const [count, setCount] = useState(0)
const [name, setName] = useState('lzw.')

// 模拟 class 组件的 DidMount 和 DidUpdate
// useEffect(() => {
// console.log('此处发送一个 Ajax 请求')
// })

// 模拟 class 组件的 DidMount
useEffect(() => {
console.log('加载完了')
}, []) // 第二个参数是 [] (不依赖任何 state)

// 模拟 class 组件的 DidUpdate
useEffect(() => {
console.log('更新了')
}, [count, name]) // 第二个参数是依赖的 state

// 模拟 class 组件的 DidMount
useEffect(() => {
// let timerId = setInterval(() => {
// console.log(Date.now())
// }, 1000)

// 返回函数
// 模拟 class 组件的 WillUnMount
return () => {
clearInterval(timerId)
}
}, [])

function setVal() {
setCount(count + 1)
setName(name + '-' + count)
}

return <div>
<p>你点击了多少次 {count}{name}</p>
<button onClick={setVal}>点击</button>
</div>
}

export default LifeCycles

useEffect 使用总结

  • 模拟 componentDidMount - useEffect 依赖 []
  • 模拟 componentDidUpdate - useEffect 依赖 [],或者依赖 [a,b]
  • 模拟 componentWillUnmount - useEffect 中返回一个函数

useEffect 让纯函数有了副作用

  • 默认情况下,执行纯函数,输入参数,返回结果,无副作用
  • 所谓副作用,就是对函数之外造成影响,如设置全局定制任务
  • 而组件需要副作用,所有需要 useEffect “钩” 到纯函数中

用 useEffect 模拟 WillUnMount 时的注意事项

先看一个好友在线的例子

使用 class 组件实现

import React from "react";

class FriendStatus extends React.Component {
constructor(props) {
super(props);

this.state = {
status: false // 默认当前不在线
}
}

render() {
return <div>
好友 {this.props.friendId} 在线状态: {this.state.status}
</div>
}

componentDidMount() {
console.log(`开始监听 ${this.props.friendId} 的在线状态`)
}

componentWillUnmount() {
console.log(`结束监听 ${this.props.friendId} 的在线状态`)
}

// friendId 更新
componentDidUpdate(prevProps) {
console.log(`结束监听 ${prevProps.friendId} 的在线状态`)
console.log(`开始监听 ${this.props.friendId} 的在线状态`)
}
}

export default FriendStatus

然后调用

import React, {useState} from "react";
import FriendStatus from "./FriendStatusClass";

function App() {
const [flag, setFlag] = useState(true)
const [id, setId] = useState(1)

return <div>
<button onClick={() => setFlag(false)}>设置 flag</button>
<button onClick={() => setId(id + 1)}>设置 ID</button>
{flag && <FriendStatus friendId={id}></FriendStatus>}
</div>
}

export default App

执行结果为

开始监听 1 的在线状态
结束监听 1 的在线状态
开始监听 2 的在线状态
结束监听 2 的在线状态

使用函数组件实现

import React, {useEffect, useState} from "react";

function FriendStatus({friendId}) {
const [status, setStatus] = useState(false)

// DidMount 和 DidUpdate
useEffect(() => {
console.log(`开始监听 ${friendId} 的在线状态`)

// 【特别注意】
// 此处并不完全等同于 willUnMount
// props 发生变化,即更新,也会执行结束监听
// 准确的说:返回函数,会在下一次 effect 执行之前,被执行
return () => {
console.log(`结束监听 ${friendId} 的在线状态`)
}
})

return <div>
好友 {friendId} 在线状态: {status.toString()}
</div>
}

export default FriendStatus

useEffect 中返回函数 fn

  • useEffect 依赖 [],组件销毁时执行 fn,等于 WillUnMount
  • useEffect 无依赖或依赖 [a,b],组件更新时执行 fn
  • 即,下一次执行 useEffect 之前,就会执行 fn,无论更新或卸载