Разъем.ввод-вывод с использованием старого значения состояния в функциональных компонентах React

#reactjs #socket.io #react-hooks

Вопрос:

Ниже приведен код, с которым я изначально работал. Насколько я могу судить, этот код работает, если компонент повторно отображает и повторно подключает сокет, но не при начальной загрузке. Я дошел до определения того , что состояние обновляется номинально, когда я использую локальные значения из вызова дочернего компонента handleUpdateCell , но, похоже, обновить значение из него невозможно socket.on() .

 interface MyComponentProps {
    gameID: number;
}

export const MyComponent: React.FC<MyComponentProps> = (props) => {
    const params = useParams<GameRouteParams>();
    const gameID = parseInt(params.gameID!);
    const [gameName, setGameName] = useState<string>();
    const [positions, setPositions] = useState<ICell[]>();
    const [lastUpdate, setLastUpdate] = useState<ICell>();
    const [pawns, setPawns] = useState<Record<string, IPawn>>();
    const [isLoading, setIsLoading] = useState(false);
const socket = useMemo(() => {
            const ENDPOINT = `http://localhost`;
            const APIPATH = `/api/source`;
            return socketIOClient(ENDPOINT, {
                path: APIPATH,
                transports: ["websocket"], //TODO: I don't like this option atm, it means I'm misunderstanding something about CORS to get this far, but I just want to move forward
                query: {
                    gameID: gameID.toString(),
                },
            });
}, [gameID]);
const handleUpdateCell: HandleUpdateCell = (id, x, y, clientDrop) => {
    console.log({ positions: positions }); //THIS IS ALWAYS undefined (initial state)FROM SOCKET.ON('map-update')
    if (positions) {
        const positionUpdate = [...positions];
        const positionIndex = positionUpdate.findIndex(
            (cell) => cell.id === id
        );
        if (positionIndex > -1) {
            const theCell = positionUpdate[positionIndex];
            theCell.x = x;
            theCell.y = y;
            positionUpdate[positionIndex] = theCell;
            setLastUpdate(theCell);
            setPositions(positionUpdate);
            if (clientDrop amp;amp; socket) {
                socket.emit(`token-update:${gameID}`, theCell);
            }
        }
    }
};

const handleMapUpdate = (cellUpdate: { positions: ICell[] }) => {
    if (cellUpdate amp;amp; cellUpdate.positions) {
        for (let i = 0; i < cellUpdate.positions.length; i  ) {
            const cell = cellUpdate.positions[i];
            handleUpdateCell(cell.id, cell.x, cell.y, false);
        }
    }
};

useEffect(() => {
    //connect via websockets to api
    socket.on("connect", () => {
        console.log("Connect");
        socket.onAny((eventName, args) => {
            console.log({ eventName, args });
        });
        socket.on(`game-init:${gameID}`, (gameInfo: IGameState) => {
            console.log("Game Init");
            console.log(gameInfo);
            setGameName(gameInfo.name);
            setPositions(gameInfo.positions);
            setPawns(gameInfo.pawns);
            setIsLoading(false);
        });
        socket.on(`map-update:${gameID}`, handleMapUpdate);
    });
}, [gameID, socket, handleMapUpdate]);
useEffect(() => {
    console.log({ Title: "Position useEffect", positions: positions });
}, [positions]);
 

Похоже, что старое состояние захвачено, поэтому я попытался добавить useCallback :

 const handleUpdateCell: HandleUpdateCell = useCallback((id, x, y, clientDrop) => {
    console.log({ positions: positions });
    if (positions) {
        const positionUpdate = [...positions];
        const positionIndex = positionUpdate.findIndex(
            (cell) => cell.id === id
        );
        if (positionIndex > -1) {
            const theCell = positionUpdate[positionIndex];
            theCell.x = x;
            theCell.y = y;
            positionUpdate[positionIndex] = theCell;
            setLastUpdate(theCell);
            setPositions(positionUpdate);
            if (clientDrop amp;amp; socket) {
                socket.emit(`token-update:${gameID}`, theCell);
            }
        }
    }
}, [positions, gameID]);

const handleMapUpdate = useCallback((cellUpdate: { positions: ICell[] }) => {
    if (cellUpdate amp;amp; cellUpdate.positions) {
        for (let i = 0; i < cellUpdate.positions.length; i  ) {
            const cell = cellUpdate.positions[i];
            handleUpdateCell(cell.id, cell.x, cell.y, false);
        }
    }
}, [
    handleUpdateCell
]);
 

К сожалению, это, похоже, не помогает. Я тоже useRef примерял positions , но это тоже был провал. Я предполагаю, что совершаю очень простую ошибку, но я нахожусь на 3-м часе отладки только этой проблемы.