Fix subtle stale state issue in H5WasmProvider
#1568
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
We received the following bug report in myHDF5:
When opening the first file then the second file, a bunch of errors are logged to the console that seem to suggest that the entity at the default path is not found due to an incorrect identifier. Important detail: both files have the same
default
NeXus path.After some investigation, I identified that the problem comes from a stale state issue in
H5WasmProvider
: upon receiving new prop values (fileName
andbuffer
), an effect runs in order to create a new API instance, update it in local state, and clean-up the old API instance. However, since this is done asynchronously, theApp
gets rendered once with the old API instance.I think the reason why this hadn't caused any issue until now is because it only occurs when switching between two files with the same default path. If the default path of the second file is different, then the old API instance can't find a corresponding entity in the file and stops there silently. On the contrary, if the default path is the same, then the old API instance finds an entity, which means that the viewer then tries to read it... but the internal ID of the entity returned by the old API instance doesn't match the ID of the entity at the same path in the second file, which throws an error.
The difficulty in finding a solution comes from the fact that the API needs to be cleaned up, which typically means
useEffect
... Unfortunately, the following solutions were not satisfactory:useState
+useEffect
) + requiring the consumer to set akey
onH5WasmProvider
👉 this would rely on the consumer to compute the key correctly (fileName
is not sufficient, since it's possible to open two files with the same name, and React doesn't accept an object likebuffer
as key)useMemo
(instead ofuseState
) +useEffect
with only a clean-up function 👉 this is not compatible withStrictMode
as it leads to the clean-up function being called right away on the memoisedapi
instance in development.In the end, I've landed on a solution that involves
useMemo
and a synchronoussetState
call. TheApp
is never rendered with a stale API instance because the synchronoussetState
call tells React to stop rendering the tree; theH5WasmProvider
component function is run to completion (so the clean-up call that follows is called as expected), but it stops there and re-renders right away.