No problem. This is from my Electron client (the mobile one does the same, but clumsier). The mobile app came first and used the package’s userTracker as-is. Expo doesn’t turn on StrictMode, so the hook’s main flaw never surfaced there, and it worked well enough that I had no reason to touch it. The Electron app came later with StrictMode on, components stopped updating, and I had to rewrite it. That’s how the Electron app got the cleaner version.
I’d still recommend my Electron’s version to anyone starting fresh, which is why I’m posting it. The package’s own hook has two flaws that my Electron app avoids. It sets state during render, which is the #179 warning. And under StrictMode its components stop updating. I just haven’t hit either on mobile so I haven’t got around to updating it.
import { useEffect, useRef, useState, type DependencyList } from 'react';
import Meteor, { Tracker } from '@meteorrn/core';
type MeteorData = { onChange(cb: () => void): void; offChange(cb: () => void): void };
const meteorData = () => (Meteor as unknown as { getData(): MeteorData }).getData();
export function useTracker<T>(reactiveFn: () => T, deps: DependencyList = []): T {
// First value computed during render, but without creating a computation.
const [value, setValue] = useState<T>(() => Tracker.nonreactive(reactiveFn));
// Always run the latest closure without restarting the computation every render.
const fnRef = useRef(reactiveFn);
useEffect(() => { fnRef.current = reactiveFn; });
useEffect(() => {
const computation = Tracker.autorun(() => setValue(fnRef.current()));
// Also re-run on any minimongo write: the package can miss a doc *leaving*
// a filtered find(). offChange needs the same function reference back.
const data = meteorData();
const onChange = () => computation.invalidate();
data.onChange(onChange);
return () => {
data.offChange(onChange);
computation.stop();
};
}, deps);
return value;
}
Use it like the package’s: const user = userTracker(() => Meteor.user()); The computation only lives in the useEffect, so render never sets state, (#179), and StrictMode’s unmount/remount leaves one live computation. The onChange part is the safety net I mentioned. It costs a local query and a re-render per batch of writes. Tracker merges invalidations, so it’s cheap.
Correction on the subscriptions I mentioned. It scales fine and the IDs are fine, and it’s not a meteorrn bug. Its own subscribe() is a straight port of Meteor core’s, and real Meteor behaves the same way. A sub is reused only when the same computation re-runs with the same name and args, so two components subbing to the same thing open two subs on the web as well. The merge box sends each document once regardless, so the cost is extra publish runs on the server, not extra data. I consolidated mine because React Navigation keeps the screens underneath mounted, and I ended up with six copies of one publication, which also meant six places to get reconnect handling right. A provider at the root that subs once and passes ready() down through context fixed both.