From 80fef10086f345b6f375682e9ebcb291d87b4b1c Mon Sep 17 00:00:00 2001 From: Kevin Newman Date: Thu, 4 Jul 2019 13:26:03 -0400 Subject: [PATCH 1/4] Use 1 useRef instead of multiple --- packages/react-meteor-data/useTracker.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/react-meteor-data/useTracker.js b/packages/react-meteor-data/useTracker.js index 82a1b36f..52c4efed 100644 --- a/packages/react-meteor-data/useTracker.js +++ b/packages/react-meteor-data/useTracker.js @@ -74,16 +74,14 @@ function areHookInputsEqual(nextDeps, prevDeps) { let uniqueCounter = 0; function useTracker(reactiveFn, deps) { - const previousDeps = useRef(); - const computation = useRef(); - const trackerData = useRef(); + const { current: refs } = useRef({}); const [, forceUpdate] = useState(); const dispose = () => { - if (computation.current) { - computation.current.stop(); - computation.current = null; + if (refs.computation) { + refs.computation.stop(); + refs.computation = null; } }; @@ -91,7 +89,7 @@ function useTracker(reactiveFn, deps) { // in order to support render calls with synchronous data from the reactive computation // if prevDeps or deps are not set areHookInputsEqual always returns false // and the reactive functions is always called - if (!areHookInputsEqual(deps, previousDeps.current)) { + if (!areHookInputsEqual(deps, refs.previousDeps)) { // if we are re-creating the computation, we need to stop the old one. dispose(); @@ -100,17 +98,17 @@ function useTracker(reactiveFn, deps) { // In that case, we want to opt out of the normal behavior of nested // Computations, where if the outer one is invalidated or stopped, // it stops the inner one. - computation.current = Tracker.nonreactive(() => ( + refs.computation = Tracker.nonreactive(() => ( Tracker.autorun((c) => { // This will capture data synchronously on first run (and after deps change). // Additional cycles will follow the normal computation behavior. const data = reactiveFn(); if (Meteor.isDevelopment) checkCursor(data); - trackerData.current = data; + refs.trackerData = data; if (c.firstRun) { // store the deps for comparison on next render - previousDeps.current = deps; + refs.previousDeps = deps; } else { // use a uniqueCounter to trigger a state change to force a re-render forceUpdate(++uniqueCounter); @@ -133,7 +131,7 @@ function useTracker(reactiveFn, deps) { return dispose; }, []); - return trackerData.current; + return refs.trackerData; } // When rendering on the server, we don't want to use the Tracker. From 8ae0db41e95d818391689759521fb751cab84906 Mon Sep 17 00:00:00 2001 From: Kevin Newman Date: Thu, 4 Jul 2019 13:36:45 -0400 Subject: [PATCH 2/4] If reactiveFn will run synchrously next render, don't bother running it asynchronously --- packages/react-meteor-data/useTracker.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/react-meteor-data/useTracker.js b/packages/react-meteor-data/useTracker.js index 52c4efed..ef37f3e6 100644 --- a/packages/react-meteor-data/useTracker.js +++ b/packages/react-meteor-data/useTracker.js @@ -93,6 +93,9 @@ function useTracker(reactiveFn, deps) { // if we are re-creating the computation, we need to stop the old one. dispose(); + // store the deps for comparison on next render + refs.previousDeps = deps; + // Use Tracker.nonreactive in case we are inside a Tracker Computation. // This can happen if someone calls `ReactDOM.render` inside a Computation. // In that case, we want to opt out of the normal behavior of nested @@ -100,16 +103,22 @@ function useTracker(reactiveFn, deps) { // it stops the inner one. refs.computation = Tracker.nonreactive(() => ( Tracker.autorun((c) => { - // This will capture data synchronously on first run (and after deps change). - // Additional cycles will follow the normal computation behavior. - const data = reactiveFn(); - if (Meteor.isDevelopment) checkCursor(data); - refs.trackerData = data; + const runReactiveFn = () => { + const data = reactiveFn(); + if (Meteor.isDevelopment) checkCursor(data); + refs.trackerData = data; + }; if (c.firstRun) { - // store the deps for comparison on next render - refs.previousDeps = deps; + // This will capture data synchronously on first run (and after deps change). + // Additional cycles will follow the normal computation behavior. + runReactiveFn(); } else { + // Only run reactiveFn if the hooks have not change, or are not falsy. + if (areHookInputsEqual(deps, refs.previousDeps)) { + runReactiveFn(); + } + // If deps have changed or are falsy, let the reactiveFn run on next render. // use a uniqueCounter to trigger a state change to force a re-render forceUpdate(++uniqueCounter); } From f68ae5be17eae82e46f391fc1cc1f4abc2ae8dd7 Mon Sep 17 00:00:00 2001 From: Kevin Newman Date: Thu, 4 Jul 2019 14:21:15 -0400 Subject: [PATCH 3/4] Dispose of the computation early, if the deps are falsy on meteor reactive changes --- packages/react-meteor-data/useTracker.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/react-meteor-data/useTracker.js b/packages/react-meteor-data/useTracker.js index ef37f3e6..7b4ab5bf 100644 --- a/packages/react-meteor-data/useTracker.js +++ b/packages/react-meteor-data/useTracker.js @@ -114,11 +114,13 @@ function useTracker(reactiveFn, deps) { // Additional cycles will follow the normal computation behavior. runReactiveFn(); } else { - // Only run reactiveFn if the hooks have not change, or are not falsy. - if (areHookInputsEqual(deps, refs.previousDeps)) { + // Only run reactiveFn if the deps or are not falsy. + if (!deps || !refs.previousDeps) { + // Dispose early, if refs are falsy - we'll rebuild and run on the next render. + dispose(); + } else { runReactiveFn(); } - // If deps have changed or are falsy, let the reactiveFn run on next render. // use a uniqueCounter to trigger a state change to force a re-render forceUpdate(++uniqueCounter); } From c58702612c5c86b6adab6193e6e6a133adf318b6 Mon Sep 17 00:00:00 2001 From: Kevin Newman Date: Fri, 5 Jul 2019 01:53:23 -0400 Subject: [PATCH 4/4] previousDeps will always equal deps at this point, so just check previousDeps --- packages/react-meteor-data/useTracker.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/react-meteor-data/useTracker.js b/packages/react-meteor-data/useTracker.js index 7b4ab5bf..fff0af45 100644 --- a/packages/react-meteor-data/useTracker.js +++ b/packages/react-meteor-data/useTracker.js @@ -114,9 +114,8 @@ function useTracker(reactiveFn, deps) { // Additional cycles will follow the normal computation behavior. runReactiveFn(); } else { - // Only run reactiveFn if the deps or are not falsy. - if (!deps || !refs.previousDeps) { - // Dispose early, if refs are falsy - we'll rebuild and run on the next render. + // If deps are falsy, stop computation and let next render handle reactiveFn. + if (!refs.previousDeps) { dispose(); } else { runReactiveFn();