You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Redux中dispatch/useSelector状态未更新及多端同步问题求助

问题解决方案

问题根源分析

当前的核心问题是组件本地状态无法跨用户同步,且房间的游戏状态没有在服务端层面做管控:

  1. 房主的inGame是组件本地useState,服务端不知道游戏已启动,所以仍允许新用户加入;
  2. 其他用户的组件没有收到游戏启动的通知,本地inGame状态还是初始的false,界面不会切换。

具体解决步骤

1. 管控房间加入权限(解决仍可加入房间的问题)

需要把房间的「游戏是否启动」状态移到服务端维护,同时通过Redux管理全局状态,在房间入口组件(Room.js)中做拦截:

  • 房主点击开始游戏时,通过Socket向服务端发送gameStarted事件,服务端标记该房间为「已启动」,拒绝后续的加入请求;
  • 在Room.js中,读取服务端同步的房间状态或Redux全局状态,当游戏已启动时,隐藏/禁用加入房间的入口。

2. 同步所有用户的界面状态(解决其他用户界面不切换的问题)

通过Socket实现跨用户的状态同步,确保所有房间内的用户都能收到游戏启动的通知:

  • 修改GameArea.js中的handleStartGame,增加Socket广播事件;
  • 在GameArea.js中监听服务端广播的游戏启动事件,触发本地状态和Redux状态的更新。

代码调整示例

调整GameArea.js

import { useState, useEffect } from "react";
import { useDispatch } from 'react-redux';

import { useAuth } from "../../contexts/AuthContext";
import { startGame, rollDices } from "../../services/store/action";
import { Game } from "./Game";

export const GameArea = ({ socket, userIds, host, roomId }) => {
    const [inGame, setInGame] = useState(false);

    const { currentUser } = useAuth();
    const dispatch = useDispatch();

    // 监听服务端广播的游戏启动事件
    useEffect(() => {
        socket.on('gameStarted', () => {
            setInGame(true);
            dispatch(startGame());
        });

        // 组件卸载时移除监听,避免内存泄漏
        return () => {
            socket.off('gameStarted');
        };
    }, [socket, dispatch]);

    const handleStartGame = () => {
        setInGame(true);
        dispatch(startGame());
        dispatch(rollDices(userIds));
        // 通知服务端游戏启动,服务端广播给所有房间用户
        socket.emit('gameStarted', { roomId });
    }

    if(!inGame) {
        if(currentUser.uid === host) {
            return (
                <button
                    className="mainButton"
                    id="startButton"
                    onClick={handleStartGame}
                >Start Game</button>
            );
        }else {
            return (<p id="waitingMsg">Waiting for the host to<br/>start the game...</p>);
        }
    }else {
        return (
            <Game socket={socket} />
        );
    }
}

调整Room.js(拦截加入逻辑)

// 假设通过Socket获取服务端的房间状态,或从Redux读取
const { gameStarted } = useSelector(state => state.game);

return (
    <div className="room-container">
        {gameStarted ? (
            <p className="join-disabled">游戏已开始,无法加入该房间</p>
        ) : (
            <button onClick={handleJoinRoom} className="join-button">加入房间</button>
        )}
    </div>
);

补充Redux Reducer(维护全局游戏状态)

const initialState = {
    gameStarted: false,
    // 其他游戏相关状态
};

export const gameReducer = (state = initialState, action) => {
    switch(action.type) {
        case 'START_GAME':
            return { ...state, gameStarted: action.payload };
        default:
            return state;
    }
};

服务端逻辑提示

服务端需要监听gameStarted事件,标记对应房间的状态为已启动,同时广播该事件给房间内所有用户,示例(以Socket.io为例):

socket.on('gameStarted', ({ roomId }) => {
    // 标记房间状态为已启动
    rooms[roomId].isStarted = true;
    // 广播给房间内所有用户
    io.to(roomId).emit('gameStarted');
});

// 处理用户加入请求时,判断房间是否已启动
socket.on('joinRoom', ({ roomId }) => {
    if(rooms[roomId]?.isStarted) {
        socket.emit('joinFailed', '游戏已开始,无法加入');
        return;
    }
    // 正常加入逻辑
});

内容的提问来源于stack exchange,提问作者Katgirl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 00:53:16