atlassian/react-sweet-state

Container cleanup occurs after remounting, clearing state set by onInit

s-taylor opened this issue · 1 comments

Scenario:

I have a component that is linked with a container and has a scope.

Events:

  • mount the component
  • onInit is fired and sets some state
  • unmount the component - triggers the state cleanup to occur, but does not execute immediately
  • mount the component (same component)
  • onInit is fired and sets some state, the state from the previous render is still present, component sets some state
  • cleanup occurs, removing the state

This situation happens within react storybooks. I tried to reproduce it in pure react using two different components linked to the same container to ensure react performs an unmount and remount when switching, but did not experience the same problem. Perhaps I'm missing something here though.

I did notice that the cleanup happens on the next tick, so in theory it should be possible to replicate it in other environments.
https://github.com/atlassian/react-sweet-state/blob/master/src/components/container.js#L79

Expected:

I would expect the cleanup to occur before mounting componentB. This option is preferred as it ensures you get clean state if you unmount the last component using a store, then remount another component using the same store.

Alternatively not perform the cleanup step at all if it would trigger after the same container is remounted. This would at least ensure the state is not destroyed, but state would be persisted across components even though the store was unmounted from the application entirely.

Example:

In my specific case, this causes a problem with using a singular component in storybooks.

https://codesandbox.io/p/sandbox/hungry-sea-ub8boh?file=%2Fsrc%2Ftest-components.story.tsx&selection=%5B%7B%22endColumn%22%3A25%2C%22endLineNumber%22%3A4%2C%22startColumn%22%3A25%2C%22startLineNumber%22%3A4%7D%5D

To see the issue, switch between the stories and view the logs.

Thank you for providing a reproducible example. Seems like the issue is due to useSyncExternalStore using useEffect for subscriptions, however those are scheduled via requestAnimationFrame + postMessage and so there is a chance that a store cleanup happens between new component being rendered and subscription happening 😵‍💫