Skip to content
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 25 additions & 16 deletions packages/react-meteor-data/useTracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,44 +74,53 @@ 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;
}
};

// this is called like at componentWillMount and componentWillUpdate equally
// 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();

// 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
// 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;
const runReactiveFn = () => {
const data = reactiveFn();
if (Meteor.isDevelopment) checkCursor(data);
refs.trackerData = data;
};

if (c.firstRun) {
// store the deps for comparison on next render
previousDeps.current = 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 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();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

else { c.stop() }

IMHO the computation should be stopped as fast as you can to avoid async related issues.
Who knows when react decides to update the component which then calls dispose().

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense - I also wonder if areHookInputsEqual(deps, refs.previousDeps) is overkill - the only real case I think we need to check for here is if either deps or refs.previousDeps are falsy, because the enclosed value of dep and the refs value should never really change between the last run (which did an equality check) and the reactive function execution. In other words, they will always be made equal before the computation runs, because if deps changes, the computation is stopped/restarted and those values made equal at the same time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, if deps and prevDeps are different, they are always handled within react's lifecycles.
I don't see any reason why !deps || !refs.previousDeps should work.

// use a uniqueCounter to trigger a state change to force a re-render
forceUpdate(++uniqueCounter);
}
Expand All @@ -133,7 +142,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.
Expand Down