react18【系列实用教程】useEffect —— 副作用操作 (2024最新版)

简介: react18【系列实用教程】useEffect —— 副作用操作 (2024最新版)

什么是副作用操作?

useEffect 用于编写由渲染本身引起的对接组件外部的操作(官方称呼为:副作用操作)

以下情况会触发页面渲染

  • 初次加载页面(组的挂载)
  • 响应式变量发生变化,触发页面根据新值重新渲染(组件更新)
  • 关闭页面(组件卸载)

以下情况需要添加副作用操作

  • 页面初步渲染完成后,向服务器获取数据完成页面的最终渲染
  • 响应式变量发生变化时,先根据新值执行必要的业务逻辑,再进行最终的页面更新渲染
  • 关闭页面时,需清除副作用,如清除定时器,移除全局事件等。

useEffect 语法

useEffect 是 hook 函数

一个参数(必要): 自定义的处理函数(官方称呼为:副作用函数),可在副作用函数中 return 一个函数来清除副作用,从第二次执行副作用函数开始,每次都会先执行return中的清除副作用的函数,再执行副作用函数,当组件卸载时,所有清除副作用的函数会按顺序依次执行。


第二个参数(可选): 依赖项 (不能为对象/数组等引用类型的数据,因为引用类型的数据每次页面渲染时都会生成新地址,若useEffect的处理函数会引发页面重新渲染,则会导致死循环)

无依赖项

执行副作用函数的时机

  • 组件初次渲染后(组件挂载完成),在开发环境,会执行两次,生产环境仅执行一次
  • 组件更新渲染前(任一响应式变量变化都会触发!)
import { useEffect } from "react";

function Demo() {
  useEffect(() => {
    console.log("执行了副作用函数");
  });
  return (
    <>
      <div>你好</div>
    </>
  );
}

export default Demo;

依赖项为空数组 []

类似 vue 的生命周期 mounted

执行副作用函数的时机

  • 组件初次渲染后(组件挂载完成),在开发环境,会执行两次,生产环境仅执行一次
import { useEffect } from "react";

function Demo() {
  useEffect(() => {
    console.log("执行了副作用函数");
  }, []);
  return (
    <>
      <div>你好</div>
    </>
  );
}

export default Demo;

使用场景

初次渲染页面时访问接口加载页面数据

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

function Demo() {
  const [list, setList] = useState([]);
  useEffect(() => {
    async function getList() {
      const res = await axios.get("http://localhost:3000/dataList");
      setList(res.data);
    }
    getList();
  }, []);

  return (
    <>
      {list.map((item) => (
        <div key={item.id}>{item.title}</div>
      ))}
    </>
  );
}

export default Demo;

依赖项为响应式变量构成的数组

类似 vue 的立即执行侦听器 watch

执行副作用函数的时机

  • 组件初次渲染后(组件挂载完成),在开发环境,会执行两次,生产环境仅执行一次
  • 响应式变量发生变化触发页面进行更新渲染前
import { useEffect, useState } from "react";

function Demo() {
  const [name, setName] = useState("朝阳");
  useEffect(() => {
    // 在挂载和 name 更新时,都会执行!
    console.log("当前name值为:", name);
  }, [name]);
  function changeName() {
    setName("晚霞");
  }
  return (
    <>
      <button onClick={changeName}>改名字</button>
    </>
  );
}

export default Demo;

清除副作用

最经典的场景即在组件卸载时清除计时器,以免内存泄露

清除副作用的逻辑,写在 useEffect 内回调函数的 return 后面,需要注意的是,return 后必须是一个函数!

father.jsx

import { useState } from "react";
import Child from "./child.jsx";

function Father() {
  const [ifChild, setIfChild] = useState(true);

  function removeChild() {
    setIfChild(false);
  }
  return (
    <>
      <button onClick={removeChild}>移除子组件</button>
      {ifChild && <Child />}
    </>
  );
}

export default Father;
iimport { useState } from "react";
import Child from "./child.jsx";

function Father() {
  const [ifChild, setIfChild] = useState(true);

  function removeChild() {
    setIfChild(false);
  }
  return (
    <>
      <button onClick={removeChild}>移除子组件</button>
      {ifChild && <Child />}
    </>
  );
}

export default Father;


child.jsx

import { useEffect } from "react";

function Child() {
  useEffect(() => {
    // 添加定时器
    const timer = setInterval(() => {
      console.log("执行了定时器");
    }, 1000);
    return () => {
      // 清除定时器
      clearInterval(timer);
    };
  });
  return (
    <>
      <div>
        <h1>我是子组件</h1>
      </div>
    </>
  );
}

export default Child;

如何关闭开发环境执行两次 ?

将 src/main.jsx 中的 <React.StrictMode> </React.StrictMode> 标签删除即可。

useEffect 演示代码

可通过下方演示代码,在控制台监听查看父子组件副作用函数和清除副作用函数的执行顺序和时机

father.jsx

import { useEffect, useState } from "react";
import Child from "./Child.jsx";

export default function Father() {
  const [showChild, setShowChild] = useState(true);
  const [num, setNum] = useState(0);

  console.log("父组件渲染");

  useEffect(() => {
    console.log("执行了父组件依赖[]的useEffect内的代码");
    return () => {
      console.log("执行了父组件依赖[]的useEffect中return的函数");
    };
  }, []);

  return (
    <div style={{ border: "1px solid", padding: "10px", margin: "10px" }}>
      <h1>父组件</h1>
      <p>num为:{num}</p>

      <button
        onClick={() => {
          setNum(num + 1);
        }}
      >
        +1
      </button>

      {showChild && <Child />}
      <button
        onClick={() => {
          setShowChild(false);
        }}
      >
        卸载子组件
      </button>

      <button
        onClick={() => {
          setShowChild(true);
        }}
      >
        挂载子组件
      </button>
    </div>
  );
}

Child.jsx

import { useEffect, useState } from "react";

function Child() {
  const [name, setName] = useState("朝阳");
  console.log("子组件渲染");

  useEffect(() => {
    console.log("执行了子组件无依赖的useEffect内的代码");
    return () => {
      console.log("执行了子组件无依赖的useEffect中return的函数");
    };
  });

  useEffect(() => {
    console.log("执行了子组件依赖 [] 的useEffect内的代码");
    return () => {
      console.log("执行了子组件依赖 [] 的useEffect中return的函数");
    };
  }, []);

  useEffect(() => {
    console.log("执行了子组件中依赖name的useEffect内的代码");
    return () => {
      console.log("执行了子组件中依赖name的useEffect中return的函数");
    };
  }, [name]);

  return (
    <div style={{ border: "1px solid", padding: "10px", margin: "10px" }}>
      <h1>子组件</h1>
      <p>名称为:{name}</p>
      <button onClick={() => setName("张三")}>改名称</button>
    </div>
  );
}

export default Child;

目录
相关文章
|
2月前
|
前端开发 JavaScript
深入理解并实践React Hooks —— useEffect与useState
深入理解并实践React Hooks —— useEffect与useState
143 1
|
11天前
|
前端开发 JavaScript
深入探索React Hooks:从useState到useEffect
深入探索React Hooks:从useState到useEffect
|
2月前
|
前端开发
React中函数式Hooks之useEffect的使用
本文通过示例代码讲解了React中`useEffect` Hook的用法,包括模拟生命周期、监听状态和清理资源。
56 2
React中函数式Hooks之useEffect的使用
|
1月前
|
前端开发 JavaScript CDN
React 教程
10月更文挑战第6天
45 3
|
2月前
|
Web App开发 前端开发 测试技术
react18基础教程系列--安装环境及packagejson文件分析
react18基础教程系列--安装环境及packagejson文件分析
|
3月前
|
前端开发 JavaScript
介绍React中的useEffect
【8月更文挑战第6天】介绍React中的useEffect
40 2
|
4月前
|
存储 前端开发 API
useEffect问题之React提供了什么来帮助确保useEffect的依赖被正确指定
useEffect问题之React提供了什么来帮助确保useEffect的依赖被正确指定
|
4月前
|
前端开发
react18【系列实用教程】Hooks 闭包陷阱 (2024最新版)含useState 闭包陷阱,useEffect 闭包陷阱,useCallback 闭包陷阱
react18【系列实用教程】Hooks 闭包陷阱 (2024最新版)含useState 闭包陷阱,useEffect 闭包陷阱,useCallback 闭包陷阱
78 0
|
4月前
|
JavaScript
react18【系列实用教程】useRef —— 创建 ref 对象,获取 DOM (2024最新版)
react18【系列实用教程】useRef —— 创建 ref 对象,获取 DOM (2024最新版)
57 0
|
4月前
|
缓存 前端开发
react18【系列实用教程】memo —— 缓存组件 (2024最新版)
react18【系列实用教程】memo —— 缓存组件 (2024最新版)
111 0