tinyplex / tinybase

The reactive data store for local‑first apps.
https://tinybase.org
MIT License
3.71k stars 77 forks source link

useCreate hooks do not work when React.StrictMode is enabled #124

Closed fspoettel closed 8 months ago

fspoettel commented 9 months ago

Describe the bug

I'm running into issues with the ui-react hooks when React.StrictMode is enabled.

The problem is that useCreate uses a combination of useMemo and a cleanup effect: https://github.com/tinyplex/tinybase/blob/e2ec6a6f503dc535245990907aa933e62112863b/src/ui-react/hooks.ts#L257-L269

Strict mode checks for this kind of issue by executing each hook twice. When strict mode re-runs the hooks, the memo'ed store is never re-created after it has been destroyed. This is described in the React docs here.

In tinybases case, this results in, for example, the relationships returned by useCreateRelationships to be empty after the hooks are re-run.

An implementation of useCreate that works for my use case (replacing useCreateRelationships) in strict mod looks like this:

export function useCreate<S extends OptionalSchemas, T extends Destroyable>(
  store: Store<S>,
  create: (store: Store<S>) => T,
  deps: DependencyList = [],
) {
  const [thing, setThing] = useState<T | undefined>(undefined);

  useEffect(() => {
    if (!thing) {
      setThing(create(store));
    }

    return () => {
      if (thing) {
        thing.destroy();
        setThing(undefined);
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [store, thing, ...deps]);

  return thing;
}

The same kind of issue is present in other hooks such as useCreatePersister that do not use useCreate under the hood.

Your Example Website or App

https://stackblitz.com/~/github.com/fspoettel/vite-tinybase-ts-react

Steps to Reproduce the Bug or Issue

In order to reproduce the issue, open the console, you should see that registered relationships are nuked from the store after persistence autoload finishes.

Expected behavior

I expect the library to work in strict mode.

Screenshots or Videos

No response

Platform

Additional context

No response

jamesgpearce commented 8 months ago

Great write up. Thanks for the report!

jamesgpearce commented 8 months ago

Interestingly, I can only repro in your code when there is that persister step with a 'then' callback. Otherwise a second store and second new relationships object get created, no problem. The 'then' callback is the thing causing the relationship Ids to be empty (even if the function body is empty).

Going to keep working on this. Thank you for sending me more puzzles :)

jamesgpearce commented 8 months ago

Anyone following along can try and fix this little riddle here. https://codepen.io/pen?template=MWxpgmE&editors=0010

jamesgpearce commented 8 months ago

@fspoettel this is a breaking change so I have had to move it to the forthcoming v5.0 release. Please try https://github.com/tinyplex/tinybase/releases/tag/v5.0.0-beta.1 and see if it works for you!