美文网首页
useEffect 闭包陷阱

useEffect 闭包陷阱

作者: 菜鸡前端 | 来源:发表于2021-10-11 10:11 被阅读0次

使用 useEffect 比较容易出现问题是闭包陷阱,尽量尝试不使用 useEffect,见第4。

1. 错误演示

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import { Button } from "antd";
import "./styles.css";

window.events = [];

window.test = function () {
  window.events.forEach(it => {
    it()
  });
}

function App() {
  const [count, setCount] = useState(0);
  const ref = useRef();

  const handleClick = (event) => {
    console.log(count);
  }
  window.events.push(handleClick);

  useEffect(() => {
    ref.current.addEventListener("click", handleScroll);
    return () => {
      ref.current.removeEventListener("click", handleScroll);
    };
  }, []);

  return (
    <div ref={ref}>
      <div>{count}</div>
      <Button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        点击
      </Button>
    </div>
  );
}

const rootElement = document.getElementById("root");

function render() {
  ReactDOM.render(<App />, rootElement);
}

render();

点击3次后,在控制台调用 test 函数:


image

在 events 中存了4个 handleClick,分别打印 0、1、2、3,分别引用了4个闭包变量 count。在使用 useEffect 时,没有没有 deps,那么当前的 handleClick,永远是第一次渲染 App 时,所创建的 handleClick。

2. 使用 useEffect 时要保证依赖正确

上述问题因为依赖的配置不正确导致的,如果 effect 中有依赖外部变量,需要添加到依赖中

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import { Button } from "antd";
import "./styles.css";

function App() {
  const [count, setCount] = useState(0);
  const ref = useRef();

  const handleClick = (event) => {
    console.log(count);
  }

  useEffect(() => {
    ref.current.addEventListener("scroll", handleClick);
    return () => {
      ref.current.removeEventListener("scroll", handleClick);
    };
    // effect 中依赖了 handleClick,
    // 每次 App reRender,创建了新的 handleClick
    // 需要添加到 deps 中,否则依赖的 handleClick 所在的作用域,永远是第一次 render,已经过期了
  }, [handleClick]);

  return (
    <div ref={ref} style={{ overflow: 'auto', height: '100vh', border: '1px solid red' }}>
      <div>{count}</div>
      <Button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        点击
      </Button>
      <div style={{ height: 20000}}></div>
    </div>
  );
}

const rootElement = document.getElementById("root");

function render() {
  ReactDOM.render(<App />, rootElement);
}

render();

3. 尝试将 effect 的依赖项,定义到 effect 内部

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import { Button } from "antd";
import "./styles.css";

function App() {
  const [count, setCount] = useState(0);
  const ref = useRef();

  useEffect(() => {
    // 将 handleClick 移到内部后,发现 effect 内 依赖了 count state
    // 将 count 添加到依赖中
    // 否则 handleClick 依赖的 count,永远是第一次 App 调用的作用域下的
    const handleClick = (event) => {
      console.log(count);
    }
    ref.current.addEventListener("scroll", handleClick);
    return () => {
      ref.current.removeEventListener("scroll", handleClick);
    };
  }, [count]);

  return (
    <div ref={ref} style={{ overflow: 'auto', height: '100vh', border: '1px solid red' }}>
      <div>{count}</div>
      <Button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        点击
      </Button>
      <div style={{ height: 20000}}></div>
    </div>
  );
}

const rootElement = document.getElementById("root");

function render() {
  ReactDOM.render(<App />, rootElement);
}

render();

4. 尽量不使用 useEffect

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import { Button } from "antd";
import "./styles.css";

function App() {
  const [count, setCount] = useState(0);
  const ref = useRef();

  const handleScroll = (event) => {
    console.log(count);
  }

  return (
    <div ref={ref} onScroll={handleScroll} style={{ overflow: 'auto', height: '100vh', border: '1px solid red' }}>
      <div>{count}</div>
      <Button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        点击
      </Button>
      <div style={{ height: 20000}}></div>
    </div>
  );
}

const rootElement = document.getElementById("root");

function render() {
  ReactDOM.render(<App />, rootElement);
}

render();

相关文章

  • useEffect 闭包陷阱

    使用 useEffect 比较容易出现问题是闭包陷阱,尽量尝试不使用 useEffect,见第4。 1. 错误演示...

  • useEffect的闭包陷阱及useInterval

    首先先看一段代码: 结果是:页面上count一直显示1;解析:useEffect的第二个参数为空数组,所以只会在组...

  • C#闭包陷阱

    在C#中,lambda(匿名委托)使用时,编译器会自动生成一个类来保存lambda中的方法以及字段,当lambda...

  • swift-闭包

    闭包 闭包定义 闭包简化 - 尾随闭包 闭包参数 闭包返回值 闭包的循环引用

  • react hook 的闭包陷阱

    上一篇文章 https://www.jianshu.com/p/272c7d36021a[https://www....

  • 闭包,闭包,闭包

    1、这家伙到底是什么? 网上关于这个的讨论的太多了太多了,有各种的举例子,但是大部分还在寻找这个答案的小伙伴对于变...

  • 闭包-Closures [swift 5.1]

    闭包的语法 尾随闭包 闭包逃离 自动闭包

  • Day7 闭包(Closures)

    本页包含内容:• 闭包表达式• 尾随闭包• 值捕获• 闭包是引用类型• 逃逸闭包• 自动闭包 1、闭包表达式 闭包...

  • Python闭包

    闭包 = 环境变量 + 函数 调用闭包内部的环境变量 闭包的经典误区 闭包与非闭包实现人类走路 非闭包 闭包

  • 闭包(closure)

    ● 闭包基础 ● 闭包作用 ● 闭包经典例子 ● 闭包应用 ● 闭包缺点 ● 参考资料 1、闭包基础 作用域和作...

网友评论

      本文标题:useEffect 闭包陷阱

      本文链接:https://www.haomeiwen.com/subject/nplloltx.html