Undocumented breaking change in v0.6.0?
See original GitHub issueHey! Version 0.6.0 changed the effect execution order, and I think this is a potentially breaking change that’s not called out in the change log. Regardless, my question is how to get the old behaviour back?
Looking at the CodeSandbox, notice that in version 0.5.2, the console shows logs in the correct order (first and second). However, from version 0.6.0 and up, the console shows logs in the reverse order (second and first).
The particular issue I ran into is that onSet()
callback is updating the authorisation token used in requests. These are called inside a component based on the existence of the atom value. So pre-0.6.0, we could safely assume that when the value exists, the token is set, but since 0.6.0, it is no longer true and I end up triggering requests without correct authorisation tokens.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:5 (2 by maintainers)
Top GitHub Comments
Yes, this change is listed in the release notes related to Recoil updates now occurring earlier. Setting Recoil state now happens right away instead of being queued for the end of the batch. This allows React and Recoil state changes to stay in sync and continues to align with React compatibility for concurrent rendering and transitions. You should not currently make assumptions about the relative ordering of atom effect observers and React effects, which is not documented.
The
onSet()
handler was not originally intended to be used to monitor and transform all atom set operations. For example, it is currently only called at the end of a batch, so it is possible that some set values may be skipped if they are collapsed in the same batch. There would also be complications if there are multiple atom effects attempting the same pattern or there were cascadingonSet()
handlers. I played with it a bit, and it would conceivably be possible for the semantics ofonSet()
to change to allow this usage… but we would really want to think of the ramifications of this, particularly for transactions and multi-versioned concurrent updates.To achieve the behavior you want another pattern may be safer and more straightforward. I’m not sure the semantics of your requests or tokens, but consider a wrapper hook for the request atom:
Or, it may be simpler/safer to use selectors to attach tokens to requests:
Depending on the exact behavior needed a slightly different pattern may be appropriate, but likely something using the above techniques would be safer than trying to monitor and transform all atom set operations with
onSet()
.Thanks @drarmstr ! That was very straightforward. I’ll add more details in case anyone else runs into the same problem, or if you know a cleaner solution.
I ended up implementing the selector roughly as follows:
Then pretty much every component works with this selector. There is one exception, and that’s the component responsible for resetting the atom state on logout. Very clean!
The other change I had to do was add an effect to
requestAtom
to callsetTokenAsSideEffect()
with the initial value. This is because on load, we don’t callset()
onrequestSelector
, so there was nothing setting the initial state. This effect replaces the “sync” effect which was causing incorrect state outlined in this issue.