dai-shi/react-suspense-fetch

Infinite loop when input is an object

volkanunsal opened this issue · 8 comments

I'm running into an issue where get doesn't use the cache if the argument input is an object. It must be because the get method on WeakMap uses object identity, right? Is there any way around this? Serializing the object perhaps?

I understand this behavior is not very intuitive, but I wanted something that works with JS garbage collection.
In the current design, you need to serialize it, or memoize it to keep object identity.

In react, the easiest is useMemo.
Otherwise, reselect or proxy-memoieze would help.
A naive Map can do too, but if it's only for react-suspense-fetch, serialization should be easier.
A counter question, can you provide a small codesandbox example, that we can work on as a concrete idea?

You mean a code example to iterate on this issue or on the PR? The code that caused infinite loop was:

const getProject = () => projects.get({ id: profile.id, token: profile.nextToken });

I ended up serializing the arguments. I also tried using a Map, and a memoized object, but changing the values of the Map didn't yield a new computation, as I hoped it would. The previous version of the Map was served from the cache. I couldn't make the memoized object work.

I mean a toy react app in codesandbox. but, never mind if that's a hustle.

useMemo would be used something like this:

const getProject = (key) => projects.get({ id: profile.id, token: profile.nextToken });

const Component = ({ profile }) => {
  const key = useMemo(() => ({ id: profile.id, token: profile.nextToken }), [profile.id, profile.nextToken])
  getProject(key)
  ...

I think if serialization works for you, it's good. You only need to evict it once it's no longer needed, depending on the use case.

Is there a better example on how to handle multiple inputs?

I'm trying to prefetch and then get from the cache but running into the infinite retrieval issue as well.

Updating the cache equality like so:

const store = createFetchStore(fetchSomething, {
    type: 'Map',
    areEqual: (a, b) => JSON.stringify(a) === JSON.stringify(b),
});

Solved the issue for me.

That seems the right solution. I would use fast-deep-equal, but either works.

A side note: Lately, I start to think about using WeakMap for inputs doesn't help DX much. areEqual is one good solution and my another project is re-built in that mind, but still not super comfortable. Now, I feel like to find another way, maybe for both projects. I will work on it some time later and your feedbacks will be very important.

we keep configurable cache type for the moment. closing.