useTracker alternatives

I came up with this simple useTracker alternative because I was frustrated by how many stuck reactives I had.

CAVEAT: Does not work with subscribe, or any reactive which needs some initial async setup to return useful data. A simple variant for those is to place the computation only in the useEffect and use deps since publications are JSONable. ~However I do not see how to implement it so that isLoading immediately switches to true when the deps change.~ That can be done with a hook that detects deps changes.

const useTrackerClientImpl = <T = any>(
	reactiveFn: IReactiveFn<T>,
	deps?: DependencyList,
	skipUpdate?: ISkipUpdate<T>,
): T => {
	const [iteration, forceUpdate] = useUniqueObject();

	const data = useMemo(() => {
		let data: T = shouldNeverBeReturned as T;

		// 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.

		Tracker.nonreactive(() =>
			Tracker.autorun((c: Tracker.Computation) => {
				data = reactiveFn(c);

		return data;
	}, deps && [iteration, ...deps]);

	useEffect(() => {
		let prevData = data;

		const computation = Tracker.nonreactive(() =>
			Tracker.autorun((c: Tracker.Computation) => {
				const nextData = reactiveFn(c);
				if (
					(!c.firstRun || !areDeepEqual(prevData, nextData)) &&
					!skipUpdate?.(prevData, nextData)
				) {
					prevData = nextData;

		// Stop the computation on deps changes or unmount.
		return () => {
	}, deps);

	assert(data !== shouldNeverBeReturned);

	return data;

The trick is to import areDeepEqual from 'react-fast-compare'; to skip the first potentially duplicated run of the useEffect computation. This is particularly problematic when not using a dependency array.