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

Infinite loop with useState + useRecoilState #2199

Open
Wxwind opened this issue Mar 10, 2023 · 1 comment
Open

Infinite loop with useState + useRecoilState #2199

Wxwind opened this issue Mar 10, 2023 · 1 comment

Comments

@Wxwind
Copy link

Wxwind commented Mar 10, 2023

Minimal Reproduction

https://stackblitz.com/edit/recoil-state?file=App.tsx

description

a and b are two object states created by useState and useRecoilState respectively. Now follow code will cause a infinite loop. But if both a and b use useState or useRecoilState, or just remove one of them from dependence array, it will work correctly.
(Notices the infinite loop may carsh your brower)

core code

import { FC, useEffect, useState } from 'react';
import { atom, useRecoilState } from 'recoil';

export const _a = atom<{ count: number } | undefined>({
  key: `counta`,
  default: undefined,
});

const Test: FC = () => {
  const [a, setA] = useRecoilState(_a);
  const [b, setb] = useState<{ count: number } | undefined>(undefined);
  useEffect(() => {
    if (!a && !b) {
      setA({ count: 1 });
      setb({ count: 1 });
    }
    return () => {
      if (a) setA(undefined);
      if (b) setb(undefined);
    };
  }, [a, b]);
  return (
    <div>
      b_count:{a?.count}
      a_count:{b?.count}
    </div>
  );
};

export default Test;
@csantos42
Copy link
Contributor

I think this might be related to some of the behavior pointed out in #759 and #1076, where Recoil state is not guaranteed to update on same render as hook state. There was work from @drarmstr to fix that behavior, but it looks like it relies on cutting edge React and so the Recoil hooks that support it are still unstable, but available.

To fix you can use useRecoilState_TRANSITION_SUPPORT_UNSTABLE() instead:

const [a, setA] = useRecoilState_TRANSITION_SUPPORT_UNSTABLE(_a);

That should fix the problem, although like you pointed out in the Stackblitz maybe the best solution is to not implicitly allow a and b to be a truthy/falsy combo in the cleanup of the effect and always check for their value together:

if (a && b) {
  setA(undefined);
  setb(undefined);
}

Hopefully the unstable API will become the base API in the near future, but we should definitely mention this behavior in the docs in the meantime since it could lead to some nasty bugs

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

2 participants