React函数组件中EventSource回调内State无法更新的问题求助
解决React EventSource回调中无法获取最新状态的问题
你判断的没错,这确实是闭包导致的问题:useEffect依赖数组为空,只会在组件挂载时执行一次,此时创建的updateRooms函数捕获的是初始的rooms空数组。后续setRooms更新状态后,旧的updateRooms并不会重新生成,所以回调里始终拿不到更新后的状态。
下面提供两种实用的解决方法:
方法一:使用setState的函数式更新
React的setState支持传入函数,该函数会接收当前最新的状态值作为参数,完全绕开闭包的限制。直接在这个函数里处理状态更新逻辑即可:
function HomePage() { const [rooms, setRooms] = useState<Room[]>([]); const getRooms = async () => { const data = await RoomService.getAll(currentPage); setRooms(data.rooms); } const updateRooms = (data: AddRoomPayload | DeleteRoomPayload) => { setRooms(prevRooms => { // prevRooms就是当前最新的房间列表 console.log(prevRooms); // 根据事件类型处理房间添加/删除 if ('room' in data) { return [...prevRooms, data.room]; } else if ('roomId' in data) { return prevRooms.filter(room => room.id !== data.roomId); } return prevRooms; }); } useEffect(() => { getRooms(); const url = new URL(process.env.REACT_APP_SSE_BASE_URL ?? ''); url.searchParams.append('topic', '/room'); const eventSource = new EventSource(url); eventSource.onmessage = e => updateRooms(JSON.parse(e.data)); // 组件卸载时关闭EventSource,避免内存泄漏 return () => eventSource.close(); }, []) ... }
方法二:用useRef保存最新状态
如果你的业务逻辑需要先读取最新状态做额外处理,再更新状态,可以用useRef来实时同步最新的rooms值:
function HomePage() { const [rooms, setRooms] = useState<Room[]>([]); // 用ref保存最新的rooms状态 const roomsRef = useRef<Room[]>([]); // 每次rooms更新时同步ref的值 useEffect(() => { roomsRef.current = rooms; }, [rooms]); const getRooms = async () => { const data = await RoomService.getAll(currentPage); setRooms(data.rooms); } const updateRooms = (data: AddRoomPayload | DeleteRoomPayload) => { // 从ref获取最新状态 const latestRooms = roomsRef.current; console.log(latestRooms); let newRooms = latestRooms; if ('room' in data) { newRooms = [...latestRooms, data.room]; } else if ('roomId' in data) { newRooms = latestRooms.filter(room => room.id !== data.roomId); } setRooms(newRooms); } useEffect(() => { getRooms(); const url = new URL(process.env.REACT_APP_SSE_BASE_URL ?? ''); url.searchParams.append('topic', '/room'); const eventSource = new EventSource(url); eventSource.onmessage = e => updateRooms(JSON.parse(e.data)); return () => eventSource.close(); }, []) ... }
额外注意
不要忘记在useEffect的返回函数里关闭EventSource,否则组件卸载后SSE连接会一直存在,造成内存泄漏。
内容的提问来源于stack exchange,提问作者Marc PIERRON
相关产品推荐
相关产品推荐

