Fix useFind duplicate documents on dep change and stabilize doc references#474
Open
vuphanse wants to merge 1 commit intometeor:masterfrom
Open
Fix useFind duplicate documents on dep change and stabilize doc references#474vuphanse wants to merge 1 commit intometeor:masterfrom
vuphanse wants to merge 1 commit intometeor:masterfrom
Conversation
… race/ordering cases
d110cb6 to
6951415
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
useFindcan produce duplicate documents in the rendered output when its dependency list changes and a collection mutation occurs between the new observer setup and the old observer teardown.The cause
The previous implementation used a custom
useSyncEffecthook that relied onuseMemoto run the effect (including observer setup) synchronously during render, while deferring the previous observer's cleanup touseEffect:When deps change, the following sequence occurs:
useMemore-runs during render -- creates a new observer synchronously.useEffect's cleanup phase, which hasn't run yet.useLayoutEffectin the same component or a child) is seen by both observers, each dispatching anaddedAtaction and producing a duplicate entry.This test case can reproduce the issue:
The fix
Replace
useSyncEffectwithuseLayoutEffect+cleanupRef:Instead of relying on
useMemofor synchronous effect execution anduseEffectfor deferred cleanup, the new implementation uses a single useLayoutEffect that immediately stops the previous observer before setting up the new one. BecauseuseLayoutEffectfires synchronously after DOM mutations but before the browser paints, the old observer is always torn down before the new one starts observing. There is no window where two observers coexist.Preserve document references across cursor recreation (
mergeInitialData):When deps change and a new cursor is created, fetchData produces fresh document objects even if the underlying data is identical. Dispatching these via refresh would cause memoized child components to re-render unnecessarily.
mergeInitialDatacompares the new data against the current state by _id using EJSON.equals. For documents that haven't changed, it reuses the previous object reference. If nothing changed at all (same count, same order, same content), it skips the dispatch entirely. This preserves referential stability for React.memo consumers.Test coverage