当前位置: 首页 > article >正文

使用useCallback引发对闭包的理解

一、先简单介绍一下闭包:

闭包是 JavaScript 中的重要概念,它指的是一个函数可以“记住”并访问其词法作用域,即使在这个函数的外部被执行。简单来说,闭包是由函数及其相关的环境组合而成的。

闭包的特性

函数内部可以访问外部变量:

  • 闭包允许一个函数访问其外部作用域中的变量,即使这个函数在外部被调用。

保持状态:

  • 闭包可以用来保持状态,因为它可以“记住”外部变量的值。

私有变量:

  • 通过闭包,可以创建私有变量,这些变量不能被外部代码直接访问,只能通过闭包内部的函数进行访问和修改。

简单示例:

匿名函数持久化存储了外部count的值(保持状态)

function createCounter() {
    let count = 0; // count 是一个私有变量

    return function() {
        count += 1; // 访问外部变量 count
        return count; // 返回当前的 count 值
    };
}

const counter = createCounter();

console.log(counter()); // 输出 1
console.log(counter()); // 输出 2
console.log(counter()); // 输出 3

二、在React中使用useState产生闭包:

import React, { useState } from 'react';

const Counter = () => {
    const [count, setCount] = useState(0);

    const increment = () => {
        setCount(count + 1); // 这里使用了捕获的 count 值
        setCount(count + 1); // 这次仍然使用的是同样的捕获值
    };

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={increment}>Increment</button>
        </div>
    );
};

export default Counter;

当你点击按钮时,increment 函数会调用 setCount 两次,但由于 count 是在函数创建时捕获的值,这导致 count 只增加 1,而不是 2。(内部访问外部变量,由于内部两次访问的count的值是一样的,所以无论调用多少次,这只导致count只能增加一次)

 解决方法:函数式的更新

所以为了避免这个问题,React采用函数形式的更新方式,以此来访问最新的状态值:

import React, { useState } from 'react';

const Counter = () => {
    const [count, setCount] = useState(0);

    const increment = () => {
        setCount(count + 1); // 这里使用了捕获的 count 值
        setCount(count + 1); // 这次仍然使用的是同样的捕获值
    };

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={increment}>Increment</button>
        </div>
    );
};

export default Counter;

三、 useCallback的依赖项:

创建一个外部的useState:

const [todoList, setTodoList] = useState([]);

为什么要添加todolist的依赖项?

使用了依赖项:

const openCheckModal = useCallback((id) => {
  setCurrentData(() => todoList.filter(item => item.id === id));
  setShowCheckModal(true);
}, [todoList]);

 查看定义:

useCallback缓存函数:

它确保只有在依赖项变化时才重新创建函数,从而避免子组件不必要的重新渲染。

解释:

由于闭包的问题,内部使用了外部变量(todolist),当外部变量更新的时候需要更新到最新的状态,换句话说,在初始化的时候 const openCheckModal 这个变量只加载了一次,由于闭包的问题,todolist存储的一直是初始化的todolist,当todolist改变的时候,此时也应该去动态更新当前这个const 变量

这样不会导致闭包的出现,也可以使得内部引用到的是外部最新的数据

在下面的例子中也使用了todolist,为什么只有第一个要添加todolist的依赖项呢?

 不使用依赖项

const addItem = useCallback((value) => {
  const dataItem = {
    id: new Date().getTime(),
    content: value,
    completed: false
  };
  setTodoList((todoList) => [...todoList, dataItem]);
  setInputShow(false);
}, []);

由于todolist形参传递,引用来自useState定义的变量,无关与addItem这个变量,也不会产生闭包,所以不需要传递todolist依赖项

总结:

当你将外部变量放入依赖数组中时,有几个要点需要理解:

  • 保持一致性: 如果在回调函数内部使用了某个外部变量(例如 props 或 state),将这个变量放入依赖数组中可以确保你使用的是最新的值。这避免了闭包问题,即在回调函数中引用的变量是旧值。
  • 不必要的重新调用: 如果依赖数组中的变量发生变化,但回调函数并没有被调用(例如,函数没有被某个事件触发),那么不会造成逻辑错误。只有在实际调用这个回调函数时,内部逻辑会基于最新的依赖值执行。

要想使用最新的值,就需要动态更新当前useCallback,添加外部变量的依赖项,以便获取最新的数据


http://www.kler.cn/a/400347.html

相关文章:

  • Vscode/Code-server无网环境安装通义灵码
  • Java项目实战II基于Java+Spring Boot+MySQL的共享汽车管理系统(源码+数据库+文档)
  • django的model时间怎么转时间戳
  • C++中的桥接模式
  • 热点更新场景,OceanBase如何实现性能优化
  • uniapp 跨域前端代理
  • 轻量云服务器:入门级云计算的最佳选择
  • JavaScript 中字符串和数组的概念解析与多角度对比区分
  • Javaweb梳理17——HTMLCSS简介
  • 《操作系统 - 清华大学》4 -2:非连续内存分配:分页
  • Virtual.lab声学模态分析和响应计算
  • Win11专业版Docker安装、配置记录
  • Flink新版Source接口源码解析
  • 用Java反射(Reflection)解释Spring Boot 中依赖注入的原理
  • idea项目运行时 java: 错误: 不支持发行版本 21
  • 个人备忘录
  • shell中的case语句和循环语句
  • 4.STM32之通信接口《精讲》之USART通信---实验串口发送程序
  • 【开源免费】基于Vue和SpringBoot的实习管理系统(附论文)
  • 一文速学---红黑树
  • html 图片转svg 并使用svg路径来裁剪html元素
  • 【机器学习】回归模型(线性回归+逻辑回归)原理详解
  • go语言中反射机制(3种使用场景)
  • 大数据-227 离线数仓 - Flume 自定义拦截器(续接上节) 采集启动日志和事件日志
  • 华为HCCDA云技术认证--网络服务
  • 视图合并机制解析 | OceanBase查询优化