Open banool opened 11 months ago
Notably this only happens at the start, once remoteGame updates or I move a piece locally to update the local state, it updates to the correct visual state.
I just managed to mitigate this issue by disabling React.StrictMode, it seems like the double update is what was making this issue appear. Good that it surfaced it, but not good bc I don't know how to fix the underlying issue.
Instead of:
const localGame = useMemo(() => new Chess(), []);
const [chessBoardPosition, setChessBoardPosition] = useState(localGame.fen());
can you try how it is done in the examples?:
const [game, setGame] = useState(new Chess());
...
<Chessboard
position={game.fen()}
...
/>
then updating the game class and using the functionality within that, instead of updating the game position and class separately
That was the first thing I tried, same issue.
jfyi that code with useMemo is also from the examples (that comes from the storyboard, plus some of the other issues in this repo).
I can provide a full repro later.
Hi @banool !
The issue more likely is somewhere here in this useEffect
.
useEffect(() => {
console.log("blah");
if (remoteGame === undefined) {
return;
}
console.log("setting");
setChessBoardPosition(gameToFen(remoteGame));
}, [remoteGame, localGame, chessBoardPosition, setChessBoardPosition]);
First of all, by directly calling
setChessBoardPosition(gameToFen(remoteGame));
you make double source of truth, because after that your localGame.fen()
and gameToFen(remoteGame)
will be different!!! Instead of that please sync your local and remote game states first and after that call setChessBoardPosition
. something like this will be fine:
localGame.load(gameToFen(remoteGame));
setChessBoardPosition(localGame.fen());
Secondly, please make sure that the dependency array of your useEffect doesn't contain extra dependencies, for example localGame
is absolutely useless there (change my mind)
Here is a repro with the latest code.
First, clone the code:
git clone https://github.com/banool/aptos-chess.git
git checkout 0964d0e4ad8fe8437da94a9e3fcdf2121debd051
Run the dev site:
pnpm install
pnpm start
Open the site: http://localhost:3000/#/0xd81a98dab67b5bd2943e85dee7a6b8026837f09b63d0f86529af55601e2570b3?network=testnet
You will see the pieces appear in the right spot and then snap back to the starting position. Disabling strict mode fixes this, implying some kind of bug that manifests only on running effects / render an extra time.
As you can see, I just have localGame
, not chessBoardPosition
. I don't know why the storyboard examples have duplicate sources of truth but I don't do that here, it seems much simpler to have just localGame
.
The logging is pretty clear, the same FEN is passed into the Chessboard for both of the renders at the end, so it shouldn't behave this way.
The relevant code from the repo: https://github.com/banool/aptos-chess/blob/main/frontend/src/pages/GamePage/MyChessboard.tsx.
Love the library, so I hate to jump on this complaint bus, but I faced a similar (slightly different, but possibly related) issue just now.
I'm creating a puzzle mode, and as with most puzzle modes, you have the first move being the opponent's move, and then you respond. There is a useEffect
that plays the next move of the mainline whenever it's the opponents turn, but after the first move, it often snaps back to the starting position. Debugging reveals that that particular useEffect
is called multiple times on the first move, and it appears that the chessboard rerenders the starting position after this useEffect gets triggered. Disabling ReactMode is not ideal for our project.
However, for anyone's future reference, I believe it may have something to do with how the Context component takes a while to match the diffs between the current and previous boards. I did come up with a somewhat hacky solution - there is a ref (for removing premoves) where the useImperativeHandle
call is after the diff-matching. I passed in the ref, and kept checking and retrying the first move until it became defined, and then for a little bit of time after that. It works, but obviously it's not an ideal solution.
I get that to get the nice piece-moving animations, it would be difficult to disable the diff-matching on the initial board. However, I would love to know if there are better ways of doing this.
@banool I believe your issue has nothing to do with the react-chessboard library.
Imo your issue comes from the fact that when you make a move, you are correctly setting the new board position in your makeAMove
function, that's why you see briefly the new position on the board. But then your useEffect
gets called because chessBoardPosition
got updated, this updates back chessBoardPosition
with the starting fen.
I'm working on a project and ran into this issue too! It is indeed caused by React's Strict Mode which intentionally mounts everything twice to help you caught / find remounting bugs during development (on production it only mounts once).
Disabling it is a workaround but obviously not ideal. The reason solution would be to find out why the library is failing to handle 2 quick re-renders correctly and then falling back to the initial position.
I tried some 'hacky workarounds' but they caused other issues with animations / flickering.
I'll try to look into it later if I get some time.
@mobeigi It can also be an error in your code like in @banool case probably. Have you shared your code somewhere so people can maybe help you ?
@mobeigi It can also be an error in your code like in @banool case probably. Have you shared your code somewhere so people can maybe help you ?
Possibly but I doubt it. I setup a simple board and used React to update the fen binded to a prop on page load. Due to strict modes double render it caused the flickering.
Sadly I didn't have time to investigate further and moved on.
Updating board state in a useEffect isn't recommended, you should initialise a board state in a useState setter and update it with events, not side effects
Same Issue here, using an api to get the game state and setting it game is then loaded in from a Context, the effect is exactly the same as banool's. Using a key "fixes" the issue but makes the Chessboard mount and unmount causing flickering instead so not ideal.
Logs (which are correct):
LHS is with a key and causes flickering when a move is made vs RHS which goes back to default:
@Gen1Code if your app is open source or you can extract this part of your code to a public repo, I can take a look into it if you want.
@Gen1Code Found your repo on your profile, I opened an issue over there with hints for where I think the issue comes from in your case.
I used to have this issue when user refreshes in online chess games. Data used to be fetched from server but position wouldn't change despite different fen value passed to the chessboard. So, I just added a delay of 200ms before the chessboard renders. Added a state loadingChessBoard and set it to false by default and set it to true after 200ms. Position updates like butter now!
I have the following code (simplified to remove unrelated stuff):
The point of this code is to update the local state of the board based on the state of the game from a remote source.
The state updates seem to be correct, but the board doesn't seem to "persist" the state I give it. Instead, it shows it briefly and then resets back to the initial state. You can see what I mean in the recording.
https://github.com/Clariity/react-chessboard/assets/7816187/dcf62c0d-1fea-46f5-9f6e-3919be7b5796
When logging to the console, I can see this:
This tells me I'm passing in the correct state of the game to my Chessboard.
I have tried removing the Flex and Box wrapping the Chessboard and that does nothing.
Setting a static boardWidth and removing that resizing hook doesn't help.
I have tried using just useState without useMemo but that doesn't help.
Given I'm passing in a certain FEN to Chessboard and it doesn't show it anyway, it tells me it is some kind of bug with the Chessboard, but I'm not too sure.
Any ideas on what I can do to fix this?
Versions: