Below is the code I originally ran with. As far as I can tell this code is functional if the component re-renders and reconnects the socket, but not on initial load. I've got as far as determining that the state is updated nominally when I'm using the local values from a child component calling handleUpdateCell, but it does not seem to be possible to update the value from 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 && socket) {
socket.emit(`token-update:${gameID}`, theCell);
}
}
}
};
const handleMapUpdate = (cellUpdate: { positions: ICell[] }) => {
if (cellUpdate && 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]);
It seems that the old state is captured so I tried adding 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 && socket) {
socket.emit(`token-update:${gameID}`, theCell);
}
}
}
}, [positions, gameID]);
const handleMapUpdate = useCallback((cellUpdate: { positions: ICell[] }) => {
if (cellUpdate && 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
]);
Unfortunately this does not seem to do the trick. I've also tried useRef on positions but that was also a bust. I assume I'm making a very basic error but I'm on my 3rd hour of debugging just this one problem.