feat(conform-react): stop relying on the state snapshot #467
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.
Background
Conform uses
useSyncExternalStore
with subjects tracked by a proxy to achieve fine-grained subscription. This works fine generally.. but not in a callback the way you might want! As an example, imagine you are trying to log the message out if the input is not empty:It will never logged anything unfortunately. Because Conform has no idea whether you need the value state and never subscribe it. The
fields.message.value
is outdated with theundefined
value. To solve this, a kinda awkward approach is to explicitly subscribe it during render:FYI, react-hook-form suffers from similar problem with proxy based subscription and you will find similar suggestions on their repository.
The reason why this happened is because
useSyncExternalStore
takes a snapshot from the form context and will use the same snapshot until a re-render is triggered. The form context is aware of the value changed internally but just the metadata is populating the outdated data from the snapshot. However, this is not a bug on React but rather an intentional design to avoid potential tearing with suspense.Solutions
I have considered 2 solutions:
Personally, I prefer solution 2 (which is what implemented in this PR) as I believe the impact of tearing is minimal in the context of a form in which the whole form will always suspense together instead of some pieces suspense while some not. It will take sometime until the react ecosystem adopts the new paradigm anyway and Conform might already reach version 2 by that time with a different design. But I would love to hear what the community think.