Skip to content
This repository has been archived by the owner on Jan 1, 2025. It is now read-only.

useRecoilCallback generates Batcher warning when called synchronously during render #2256

Open
nebrius opened this issue Jul 9, 2023 · 0 comments

Comments

@nebrius
Copy link

nebrius commented Jul 9, 2023

When I use and invoke useRecoilCallback synchronously in a component render, I get the following warning in the console: "Cannot update a component (Batcher) while rendering a different component (App)."

Here's some basic code that reproduces the bug:

import { useRef } from 'react';
import { RecoilRoot, atom, useRecoilCallback, useRecoilValue } from 'recoil';

const myAtom = atom<number>({
  key: 'myAtom',
});

function MyComponent() {
  const myValue = useRecoilValue(myAtom);
  return <>{myValue}</>;
}

function App() {
  const setBootstrapData = useRecoilCallback(
    ({ snapshot, gotoSnapshot }) =>
      () => {
        gotoSnapshot(
          snapshot.map(({ set }) => {
            set(myAtom, 10);
          }),
        );
      },
    [],
  );

  const isInitializedRef = useRef(false);
  if (!isInitializedRef.current) {
    setBootstrapData();
  }

  return <MyComponent />;
}

export default function AppRoot() {
  return (
    <RecoilRoot>
      <App />
    </RecoilRoot>
  );
}

And here's a codesandbox link: https://codesandbox.io/s/keen-haibt-3ywssy?file=/src/App.tsx

The reason I'm initializing my code this way, instead of using initializeState on RecoilRoot, is that I'm trying to build support for nested runtime bootstrap data as part of my library recoil-bootstrap. The full motivation can be read at https://github.com/nebrius/recoil-bootstrap#motivation. The tl;dr is that many Next.js apps have 2+ sets of bootstrap data that need to be initialized: one that is common to all pages, and one that is page specific.

The page specific one is the sticking point because the code with RecoilRoot is often in a common page wrapper (e.g. pages/_app.tsx in Next.js), and thus the only way to initialize page specific atoms inside of initializeState is to import every atom on every page, meaning we're initializing many atoms that aren't being used. (dynamic imports don't work cause tl;dr they're async and we have to initialize these synchronously for them to work in SSR). Thus, I need to initialize them separately elsewhere.

Notes:

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant