Skip to content

fix: useTracker should only render once on mount (closes #439)#482

Open
nicolv wants to merge 2 commits into
meteor:masterfrom
nicolv:fix/useTracker-double-render
Open

fix: useTracker should only render once on mount (closes #439)#482
nicolv wants to merge 2 commits into
meteor:masterfrom
nicolv:fix/useTracker-double-render

Conversation

@nicolv
Copy link
Copy Markdown

@nicolv nicolv commented Apr 28, 2026

Summary

Closes #439 - useTracker renders two times at mount

Problem

The previous implementation of useTrackerNoDeps would:

  1. Stop and recreate the Tracker computation on every render
  2. Combined with React 18 StrictMode double-rendering, this caused the component to render more times than expected

Solution

  1. Added useSyncEffect hook - A custom hook that runs effects synchronously during render but defers cleanup by 1 second to avoid StrictMode double-rendering issues

  2. Simplified useTrackerNoDeps - Now delegates to useTrackerWithDeps instead of having its own complex render-phase logic

  3. Updated useTrackerWithDeps - Uses useSyncEffect to manage the Tracker computation lifecycle, avoiding the stop/recreate on every render

Changes

  • packages/react-meteor-data/useSyncEffect.ts - New file: the useSyncEffect hook
  • packages/react-meteor-data/useTracker.ts - Simplified to use useSyncEffect

Key Technical Points

  • The previous code used Meteor.defer() and render-phase computation creation, which caused issues with StrictMode
  • useSyncEffect runs the effect in useMemo (during render) but delays cleanup by 1 second, avoiding StrictMode double-render issues
  • This is the same approach as PR [WIP] simpler useTracker #436 but refined

Testing

The existing noDepsTester test in useTracker.tests.js already covers StrictMode behavior.


Related to #439

@StorytellerCZ
Copy link
Copy Markdown
Collaborator

Looks elegant using new hooks. I'll try to find some time to test this out.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to prevent extra initial renders (especially under React 18 StrictMode) by stabilizing the Tracker computation lifecycle in useTracker, introducing a shared useSyncEffect helper and simplifying the no-deps path.

Changes:

  • Added a new useSyncEffect hook to run an effect during render while deferring cleanup.
  • Refactored useTrackerWithDeps to use useSyncEffect for creating/stopping the Tracker computation.
  • Simplified useTrackerNoDeps to delegate to useTrackerWithDeps.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
packages/react-meteor-data/useTracker.ts Refactors Tracker computation lifecycle to use useSyncEffect, and simplifies the no-deps path.
packages/react-meteor-data/useSyncEffect.ts Introduces a render-phase effect helper with deferred cleanup intended to mitigate StrictMode behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +81 to +83
const useTrackerNoDeps = <T = any>(reactiveFn: IReactiveFn<T>, skipUpdate: ISkipUpdate<T> = null) => (
useTrackerWithDeps(reactiveFn, undefined, skipUpdate)
)
@@ -2,6 +2,7 @@ declare var Package: any
import { Meteor } from 'meteor/meteor';
import { Tracker } from 'meteor/tracker';
import { useReducer, useEffect, useRef, useMemo, DependencyList } from 'react';
Comment on lines +9 to +22
const useSyncEffect = (effect: () => any, deps: DependencyList) => {
const [cleanup, timeoutId] = useMemo(
() => {
const cleanup = effect();
const timeoutId = setTimeout(cleanup, 1000);
return [cleanup, timeoutId];
},
deps
);

useEffect(() => {
clearTimeout(timeoutId);
return cleanup;
}, [cleanup, timeoutId]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

useTracker renders two times at mount, would be nice if it renders only once

3 participants