Issue with useTracker

We liked the idea of useTracker but we got into issues using it.

Warrning: Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

Such errors occurred at different components. One of components (a bit stripped down, for simplicity):

const useChannels = (
  selector?: Mongo.Selector<ChannelItem>
): [boolean, ChannelItem[]] => useTracker(() => {
  const ready = Meteor.subscribe('channels').ready();
  const channels = Channels.find(selector || {}).fetch();
  return [ready, channels];
}, [JSON.stringify(selector)]);

const ActionText = ({ action }: ActionTextProps) => {
  const [channelIds, setChannelIds] = useState<string[]>([]);
  const [, channels] = useChannels({ _id: { $in: channelIds } });

  useEffect(() => {
    const { recipients } = action;
    const list = recipients
      .filter(isChannelOrGroup)
      .map(({ channelOrGroupId }) => channelOrGroupId);
    setChannelIds(list);
  }, [action, action?.recipients]);

  // ...

  return (
    <>
      {channels.map((channel) => ...)}
    </>
  );
};

If I remove selector { _id: { $in: channelIds } } - it works fine without warnings.

1 Like

what exactly does the error come from? does it come from inside useTracker function or in your ActionText component?

There is just a little information about this warning. It complains on ActionText component, where error is coming from. But there are no useful details that could give a clue.

There are no other useEffects or setStates in this component. I have many more more complicated components and we have similar errors. These errors started showing up when we started utilize useTracker. I am tried to investigate but so far no ideas how to avoid them.

ActionText is a tiny component so I hope that it will be easier to find what’s wrong…

PS
We are still on Meteor 2.9.1

Maybe try to safe guard all those variables such as:

const channels = Channels.find(selector || {}).fetch() || []
// ... 

const { recipients } = action || {}

// ...

const list = recipients?
      .filter(isChannelOrGroup)?
      .map(({ channelOrGroupId }) => channelOrGroupId);
// ...

return (
    <>
      {channels?.map((channel) => ...)}
    </>
  );

Perhaps there is a variable such as an object that has not yet been initialized while the keys are being referenced.

const channels = Channels.find(selector || {}).fetch() ||
fetch() always return an array. Chrome DevTools warns that setState is called upon unmounted component.

In fact this is not an error. There is a discussion about this warning - Remove the warning for setState on unmounted components by gaearon · Pull Request #22114 · facebook/react · GitHub

As far as I understood the warning was removed in React 18.

And another strange thing about useTracker is - why is the callback called 3 times:

This error normally happens when the page updates the component with fetched data and the component is already unmounted. We had lots of this when running our End-To-End tests since the tests are very fast, and the page is tested and passed before the other components receive their respective data and are already unmounted. To solve this, we normally use a isMounted variable to check if the component is still mounted.

This also happens when your page/components are re-rendering. Therefore, the component is being mounted/unmounted before the completion of fetching the data.

That is one sign that the component is re-rendering and therefore, mount/unmount is happening on your component very fast resulting to the first error

Solution: prevent your component from re-rendering
How: Google has a lot of articles explaining how

Agree. That wasn’t supposed to be an error. But sometimes, it shows some issues with unwanted re-rendering e.g., it might result in multiple queries to your database as an example

To solve this, we normally use a isMounted variable to check if the component is still mounted.

That was the first thing I tried. It didn’t help.

This also happens when your page/components are re-rendering. Therefore, the component is being mounted/unmounted before the completion of fetching the data.

That’s much more likely. When I use filter my component is being unmounted and mounted one or more time and this leads to the warning I got.

But I have a number of other cases, where I switch to a page wait for a while and then switch to another page and get the same warning… Well, need to check for re-renders…

But I still don’t understand why I didn’t have this issue with withTracker.