When useAtomSelector's useEffect's cleanup function runs, it can destroy any ttl: 0 atoms before the useEffect body generates the new cached selector that should be keeping those atoms around.
While this extra destruction/recreation shouldn't usually cause problems, it can lead to a bug when such an atom is created outside React, given updated state, and then subscribed to by only the selector that is then destroyed. The best practice is to not give ttl (especially not ttl: 0) to atoms you intend to pre-cache or manage outside React. But still, Zedux can at least not make things worse in this case.
Use queueMicrotask very strategically to work around React's timing here. Defer cleanup of the old selector cache until the new effect run has subscribed to the new selector cache.
Also fix the annoying double renders and double selector invocations we added with the recent selector changes. It was very complex to work around those, so I've been putting it off. But I finally got it using:
a couple state machines stored in a couple places (locally in the component body, on a useState()[1] "ref", and in the Selectors class's new _storage property).
another queueMicrotask that cleans up all the mess this causes
These work around React StrictMode's behavior of completely destroying the first-rendered component before any effects can run.
Also clean up Selectors#_storage on ecosystem wipe and update tests.
Description
When
useAtomSelector
'suseEffect
's cleanup function runs, it can destroy anyttl: 0
atoms before theuseEffect
body generates the new cached selector that should be keeping those atoms around.While this extra destruction/recreation shouldn't usually cause problems, it can lead to a bug when such an atom is created outside React, given updated state, and then subscribed to by only the selector that is then destroyed. The best practice is to not give
ttl
(especially notttl: 0
) to atoms you intend to pre-cache or manage outside React. But still, Zedux can at least not make things worse in this case.Use
queueMicrotask
very strategically to work around React's timing here. Defer cleanup of the old selector cache until the new effect run has subscribed to the new selector cache.Also fix the annoying double renders and double selector invocations we added with the recent selector changes. It was very complex to work around those, so I've been putting it off. But I finally got it using:
useState()[1]
"ref", and in the Selectors class's new_storage
property).queueMicrotask
that cleans up all the mess this causesThese work around React StrictMode's behavior of completely destroying the first-rendered component before any effects can run.
Also clean up
Selectors#_storage
on ecosystem wipe and update tests.