-
Notifications
You must be signed in to change notification settings - Fork 158
Commit
Also re-adds an attempt to retain the "side-effect" we are creating during first render, when we can, to both versions of the hook
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,48 +66,54 @@ const useTrackerNoDeps = <T = any>(reactiveFn: IReactiveFn<T>, skipUpdate: ISkip | |
// it stops the inner one. | ||
Tracker.nonreactive(() => Tracker.autorun((c: Tracker.Computation) => { | ||
refs.computation = c; | ||
const data = reactiveFn(c); | ||
if (c.firstRun) { | ||
// Always run the reactiveFn on firstRun | ||
refs.trackerData = reactiveFn(c); | ||
} else if (!skipUpdate || !skipUpdate(refs.trackerData, reactiveFn(c))) { | ||
refs.trackerData = data; | ||
} else if (!skipUpdate || !skipUpdate(refs.trackerData, data)) { | ||
// For any reactive change, forceUpdate and let the next render rebuild the computation. | ||
forceUpdate(); | ||
} | ||
})); | ||
|
||
// To avoid creating side effects in render with Tracker when not using deps | ||
// create the computation, run the user's reactive function in a computation synchronously, | ||
// then immediately dispose of it. It'll be recreated again after the render is committed. | ||
// To clean up side effects in render, stop the computation immediately | ||
if (!refs.isMounted) { | ||
// We want to forceUpdate in useEffect to support StrictMode. | ||
// See: https://github.com/meteor/react-packages/issues/278 | ||
if (refs.computation) { | ||
refs.computation.stop(); | ||
delete refs.computation; | ||
} | ||
Meteor.defer(() => { | ||
if (!refs.isMounted && refs.computation) { | ||
refs.computation.stop(); | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
Grubba27
Contributor
|
||
delete refs.computation; | ||
} | ||
}); | ||
} | ||
|
||
useEffect(() => { | ||
// Let subsequent renders know we are mounted (render is committed). | ||
refs.isMounted = true; | ||
|
||
// Render is committed. Since useTracker without deps always runs synchronously, | ||
// forceUpdate and let the next render recreate the computation. | ||
if (!skipUpdate) { | ||
forceUpdate(); | ||
} else { | ||
Tracker.nonreactive(() => Tracker.autorun((c: Tracker.Computation) => { | ||
refs.computation = c; | ||
if (!skipUpdate(refs.trackerData, reactiveFn(c))) { | ||
// For any reactive change, forceUpdate and let the next render rebuild the computation. | ||
forceUpdate(); | ||
} | ||
})); | ||
// In some cases, the useEffect hook will run before Meteor.defer, such as | ||
// when React.lazy is used. In those cases, we might as well leave the | ||
// computation alone! | ||
if (!refs.computation) { | ||
// Render is committed, but we no longer have a computation. Invoke | ||
// forceUpdate and let the next render recreate the computation. | ||
if (!skipUpdate) { | ||
forceUpdate(); | ||
} else { | ||
Tracker.nonreactive(() => Tracker.autorun((c: Tracker.Computation) => { | ||
const data = reactiveFn(c); | ||
refs.computation = c; | ||
if (!skipUpdate(refs.trackerData, data)) { | ||
// For any reactive change, forceUpdate and let the next render rebuild the computation. | ||
forceUpdate(); | ||
} | ||
})); | ||
} | ||
} | ||
|
||
// stop the computation on unmount | ||
return () =>{ | ||
refs.computation?.stop(); | ||
delete refs.computation; | ||
} | ||
}, []); | ||
|
||
|
@@ -121,6 +127,7 @@ const useTrackerWithDeps = <T = any>(reactiveFn: IReactiveFn<T>, deps: Dependenc | |
reactiveFn: IReactiveFn<T>; | ||
data?: T; | ||
comp?: Tracker.Computation; | ||
isMounted?: boolean; | ||
}>({ reactiveFn }); | ||
|
||
// keep reactiveFn ref fresh | ||
|
@@ -140,29 +147,31 @@ const useTrackerWithDeps = <T = any>(reactiveFn: IReactiveFn<T>, deps: Dependenc | |
refs.comp = comp; | ||
// To avoid creating side effects in render, stop the computation immediately | ||
Meteor.defer(() => { | ||
if (refs.comp) { | ||
if (!refs.isMounted && refs.comp) { | ||
refs.comp.stop(); | ||
delete refs.comp; | ||
} | ||
}); | ||
}, deps); | ||
|
||
useEffect(() => { | ||
if (refs.comp) { | ||
refs.comp.stop(); | ||
delete refs.comp; | ||
// Let subsequent renders know we are mounted (render is committed). | ||
refs.isMounted = true; | ||
|
||
if (!refs.comp) { | ||
refs.comp = Tracker.nonreactive( | ||
() => Tracker.autorun((c) => { | ||
const data: T = refs.reactiveFn(c); | ||
if (!skipUpdate || !skipUpdate(refs.data, data)) { | ||
refs.data = data; | ||
forceUpdate(); | ||
} | ||
}) | ||
); | ||
} | ||
const computation = Tracker.nonreactive( | ||
() => Tracker.autorun((c) => { | ||
const data: T = refs.reactiveFn(c); | ||
if (!skipUpdate || !skipUpdate(refs.data, data)) { | ||
refs.data = data; | ||
forceUpdate(); | ||
} | ||
}) | ||
); | ||
|
||
return () => { | ||
computation.stop(); | ||
refs.comp.stop(); | ||
}; | ||
}, deps); | ||
|
||
|
3 comments
on commit 384da2f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to cause useTrackerWithDeps to re-render way too much (2.4.0 is fine, bug appears in 2.5.0)
#382
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yched I wonder whether this is true with every version of React, or just with a specific range of versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this only be called on the computation created just above? Could
refs.computation
potentially be something else than the computation created just above?