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 是怎么实现总是读取到最新的状态值的?
这里就看你对 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); }, []);}
你可以理解为保证函数引用,但是函数内部状态依旧更新,类似这段伪代码
function useEffectEvent(handler) { const handlerRef = useRef(null); useLayoutEffect(() => { handlerRef.current = handler; }); return useCallback((...args) => { const fn = handlerRef.current; return fn(...args); }, []);}
在 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指令执行过程是怎么样的?
如果是普通的云服务器,假如在服务器上部署了一个应用,并对数据库执行 CRUD 操作,那么这个时候后台应用和数据库都是长时间运行的,不同的用户请求都指向该服务器,应用的代码执行和数据库的数据存储都发生在该服务器上。如果使用云函数开发,不同的用户请求是指向同一个服务器还是根据用户的位置安排不同的服务器来处理对应的云函数? 如果云函数是类似 CDN 一样在边缘执行,是不是涉及到云函数的代码分发和冷启动,
使用 https://cdn.jsdelivr.net/npm/react@18.3.1/+esm 有问题不报错。 官方示例,下面的是正常的。会报一个未使用 key 的错误。