useEffect中如何获取更新后的state值?打印users始终为初始空数组
问题原因
该问题是React钩子的闭包陷阱导致:
传入useEffect的空依赖数组表示这个副作用只会在组件首次渲染时执行1次,绑定的事件回调捕获的users变量永远是首次渲染时的初始值[],后续setUsers更新状态触发组件重渲染,也不会更新已经绑定好的事件回调里的闭包变量。
解决方案
方案1:用useRef存储最新状态(最推荐,不需要改动Pusher初始化逻辑)
用useRef作为容器存储最新的users值,ref的current属性是可变的,不会受闭包限制:
import { useState, useEffect, useRef } from 'react'; let [users, setUsers] = useState([]); // 新增ref存储最新users const usersRef = useRef(users); // 监听users变化,同步更新ref useEffect(() => { usersRef.current = users; }, [users]); useEffect(() => { axios.get('/chat_user_list').then((res) => { setUsers(res.data); }); let pusher = new Pusher('api-key-over-here', { cluster: 'ap1', authEndpoint: "/admin/broadcasting/auth", auth: { headers: { "X-CSRF-Token": csrfToken, "Accept": "application/json" }, }, }); let channel = pusher.subscribe(`presence-room`); channel.bind("pusher:member_added", (member) => { // 从ref中取最新的users console.log(usersRef.current); // 需要更新状态可以直接写为 // setUsers([...usersRef.current, member.info]) }); // 组件卸载时清理连接避免内存泄漏 return () => { channel.unbind("pusher:member_added"); pusher.unsubscribe(`presence-room`); pusher.disconnect(); } }, []);
方案2:使用setState的函数式更新(仅适用于需要基于最新值更新状态的场景)
如果你在回调里只需要基于最新的users更新状态,不需要读取值做其他逻辑,可以直接用setState的函数式写法,回调参数自动拿到最新的前序状态:
channel.bind("pusher:member_added", (member) => { setUsers(prevUsers => { // prevUsers就是最新的users状态值 console.log(prevUsers); return [...prevUsers, member.info]; }) });
方案3:给useEffect添加依赖+清理逻辑
把users加入useEffect依赖数组,每次users变化时重新绑定事件,要注意加清理函数解绑旧事件避免重复绑定:
useEffect(() => { axios.get('/chat_user_list').then((res) => { setUsers(res.data); }); let pusher = new Pusher('api-key-over-here', { cluster: 'ap1', authEndpoint: "/admin/broadcasting/auth", auth: { headers: { "X-CSRF-Token": csrfToken, "Accept": "application/json" }, }, }); let channel = pusher.subscribe(`presence-room`); const handleMemberAdd = (member) => { console.log(users); } channel.bind("pusher:member_added", handleMemberAdd); // 每次重新执行副作用前先清理旧的连接和事件 return () => { channel.unbind("pusher:member_added", handleMemberAdd); pusher.unsubscribe(`presence-room`); pusher.disconnect(); } }, [users]);
该方案不推荐,因为每次users变化都会重新初始化Pusher连接,会产生不必要的性能开销。
内容的提问来源于stack exchange,提问作者kundefine
相关产品推荐
相关产品推荐

