在useEffect回调中获取最新状态:自制useEvent替代钩子是否合理?
useEffect回调读取最新状态/属性的替代方案探讨
背景
useEvent 可以解决在 useEffect 内部回调中读取最新 props/state 的问题,但截至2022年11月仍无法用于生产环境,其使用场景在React Beta版文档中有记载。
问题场景
const SomeComponent = ({ prop1, ...}) => { const [state, setState] = useState('initial') useEffect(() => { // 希望在connect回调中获取最新的state和props,同时不想因state变化重连 // 这里的事件处理器依赖prop1和state const connection = createConnection(...) connection.on('connect', () => { // 需要引用prop1和state的最新值 // 如果不把这些变量加入依赖数组,effect无法获取最新值 }) return () => { connection.disconnect() } }, [])
上述代码中,若将prop1和state加入useEffect的依赖数组,会触发不必要的重连;但不加入的话,回调又无法获取最新的变量值。
基于useRef的临时解决方案
const SomeComponent = ({ prop1, ...}) => { const [state, setState] = useState() const someFunction = () => { // 使用最新的prop1和state } const someFunctionRef = useRef() someFunctionRef.current = someFunction; useEffect(() => { const someFunctionRefWrapper = () => { someFunctionRef.current() } // 需要读取最新的props/state,但不想因这些值变化触发重渲染 const connection = createConnection(...) connection.on('connect', someFunctionRefWrapper) return () => { connection.disconnect() } }, [])
自定义替代钩子
由于useEvent无法用于生产环境,编写了如下自定义钩子:
const usePoorMansUseEvent = (callback) => { const itemRef = useRef(); itemRef.current = callback; const stableReference = useCallback((...args) => { itemRef.current(...args) }, []); return stableReference; }
问题解答
是不是在重复造轮子?
没错,你写的这个钩子本质就是useEvent的核心实现——用useRef实时保存最新的回调,再通过useCallback返回一个引用稳定的包装函数,调用时始终执行最新的回调逻辑。从功能上看,和官方useEvent的临时替代思路完全一致。
有没有更优的解决方案?
在useEvent正式发布并可用于生产前,这个方案已经是社区公认的最佳临时替代方案之一,没有太多更优的选择。不过可以注意几个细节:
- 确保回调函数本身不会捕获过时变量(你的写法已经通过ref规避了这个问题);
- 如果回调包含异步逻辑,要注意
itemRef.current可能在异步执行过程中被更新的情况,不过大部分场景下这个问题影响有限; - 部分第三方工具库也提供了类似能力,但核心逻辑和你写的钩子没有区别,没必要额外引入依赖增加项目复杂度。
内容的提问来源于stack exchange,提问作者Azzy
相关产品推荐
相关产品推荐

