当前位置: 首页 > 知识库问答 >
问题:

react.js - react 里面的 useEffectEvent 的执行原理是怎样的?

亢雅懿
2024-03-06
import { useState, useEffect } from 'react';import { experimental_useEffectEvent as useEffectEvent } from 'react';export default function App() {  const [position, setPosition] = useState({ x: 0, y: 0 });  const [canMove, setCanMove] = useState(true);  const onMove = useEffectEvent(e => {    if (canMove) {      setPosition({ x: e.clientX, y: e.clientY });    }  });  useEffect(() => {    window.addEventListener('pointermove', onMove);    return () => window.removeEventListener('pointermove', onMove);  }, []);  return (    <>      <label>        <input type="checkbox"          checked={canMove}          onChange={e => setCanMove(e.target.checked)}        />        The dot is allowed to move      </label>      <hr />      <div style={{        position: 'absolute',        backgroundColor: 'pink',        borderRadius: '50%',        opacity: 0.6,        transform: `translate(${position.x}px, ${position.y}px)`,        pointerEvents: 'none',        left: -20,        top: -20,        width: 40,        height: 40,      }} />    </>  );}

useEffect 的依赖项使用了 [],那么内部的 effect 函数只会在初始渲染时执行,那么监听的 onMove 函数应该第一次组件函数执行时创建的 onMove,如果把 useEffectEvent 去掉,那么 canMove 也应该是第一次渲染时使用的 canMove,之后切换 canMove 是不会改变 canMove 的值的。如果使用了 useEffectEvent, 那么 canMove 会跟着切换的 canMove 变化的,useEffectEvent 是怎么实现总是读取到最新的状态值的?

共有3个答案

郑曜灿
2024-03-06

这里就看你对 ref 的理解深不深了,ref 本质上就是一块共享内存

基于共享内存的思想,用 useRef 存储最新的函数 + useCallback 返回包装函数的固定引用就能实现

在包装函数中调用 ref 中的新函数即可

import { useCallback, useRef } from 'react';export default function useEvent<T extends Function>(callback: T) {    const ref = useRef<T>();    ref.current = callback;    return useCallback((...args) => {        return ref.current?.(...args);    }, []);}
张翰海
2024-03-06

你可以理解为保证函数引用,但是函数内部状态依旧更新,类似这段伪代码

function useEffectEvent(handler) {  const handlerRef = useRef(null);  useLayoutEffect(() => {    handlerRef.current = handler;  });  return useCallback((...args) => {    const fn = handlerRef.current;    return fn(...args);  }, []);}
陶涵育
2024-03-06

在 React 中,useEffect 是一个内建的 Hook,用于在函数组件中执行副作用操作。然而,useEffectEvent 并不是 React 的官方 API。你提到的 useEffectEvent 可能是某个库或实验性功能的一部分,或者是自定义的 Hook。

在标准的 React 中,useEffect 的行为是这样的:它会接收一个函数和一个依赖项数组作为参数。当组件渲染时,这个函数会被调用,并且它的依赖项会被用于决定是否再次执行该函数。如果依赖项数组中的任何一个值发生变化,函数就会被再次调用。如果依赖项数组为空([]),则函数只会在组件首次渲染时执行一次。

在你的示例中,useEffect 的依赖项数组为空,因此它只会在组件首次渲染时执行一次,添加 pointermove 事件监听器,并且使用当时的 onMove 函数作为回调。由于 onMove 函数在首次渲染时创建,并且 canMove 状态在后续渲染中不会改变 onMove 函数,因此 onMove 函数内部使用的 canMove 值将保持在组件首次渲染时的值。

要实现类似于你描述的行为,使得 canMove 总是保持最新值,你需要让 onMove 函数在每次 canMove 变化时重新创建。这可以通过将 onMove 函数放在 useEffect 的依赖项数组中来实现,或者使用 useCallback Hook 来确保函数引用在依赖项变化时更新。

例如,你可以这样修改代码:

import { useState, useEffect, useCallback } from 'react';export default function App() {  const [position, setPosition] = useState({ x: 0, y: 0 });  const [canMove, setCanMove] = useState(true);  const onMove = useCallback(e => {    if (canMove) {      setPosition({ x: e.clientX, y: e.clientY });    }  }, [canMove]); // 添加 canMove 作为依赖项  useEffect(() => {    window.addEventListener('pointermove', onMove);    return () => window.removeEventListener('pointermove', onMove);  }, [onMove]); // 添加 onMove 作为依赖项  // ... 其余代码保持不变}

这样,每当 canMove 状态变化时,onMove 函数都会重新创建,并且包含最新的 canMove 值。因此,无论何时触发 pointermove 事件,onMove 函数都会使用最新的 canMove 值。

 类似资料:
  • 问题内容: 作者:HappyLittleFish 问题答案:

  • RefHandle是子组件通过钩子useImperativeHandle中返回的对象类型声明,即抛给父组件中的ref上的属性的类型声明 为什么这个泛型是这样的, 这俩个泛型参数顺序不能更换, 我有点看不懂

  • 本文向大家介绍请谈一下Spring MVC的工作原理是怎样的? 相关面试题,主要包含被问及请谈一下Spring MVC的工作原理是怎样的? 时的应答技巧和注意事项,需要的朋友参考一下 考察点:设计模式 参考回答: ①客户端的所有请求都交给前端控制器DispatcherServlet来处理,它会负责调用系统的其他模块来真正处理用户的请求。 ② DispatcherServlet收到请求后,将根据请求

  • redis的get指令执行过程是怎么样的?

  • 本文向大家介绍一文告诉你Sql的执行顺序是怎样的,包括了一文告诉你Sql的执行顺序是怎样的的使用技巧和注意事项,需要的朋友参考一下 学过 Sql,或了解过 Sql 的人,应该都会写下面这行代码: 上面代码表示查询 t 表中的所有信息,是 Sql 查询中最基础,最简单的一行代码,你可以把它理解成是其他编程语言中的Hello World。 select *只是你迈入 Sql 大门的第一步,在真实工作中

  • 问题内容: 假设我已经从使用过的代码中创建了 当我们运行可执行jar时,没有控制台。那么,这条线怎么办?如何处理这种情况? 编辑01 : 注意 :这种情况是我既不使用控制台运行也不以任何方式将任何控制台与其关联。 编辑02 :使事情更清楚: 我知道什么都不会打印,因为没有控制台..!我想知道在这种情况下如何处理这条线?生成可执行jar的字节码时,是否省略此行?还是在没有控制台的情况下忽略了这一行?