Skip to content

用 useEffect 模拟组件生命周期

useEffect

让函数组件模拟生命周期

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

例子

js
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 组件实现

js
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;

然后调用

js
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;

执行结果为

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

使用函数组件实现

js
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,无论更新或卸载

基于 MIT 许可发布