Search code examples
recoiljs

How to dynamically assign data to an atomFamily with Recoil?


If I fetch dynamic data, how can I put it into an atomFamily without violating the rule of hooks? useSetRecoilState requires me to pass in atomFamily(someIndex), but with dynamic data I don't have the someIndex until after fetching the data. So it seems like I should have const setterFactory = someKey => useSetRecoilState(useAtomFamily(someKey)) but that violates the rule of hooks.

Example:

Assume I have an API response with accompanying type (and recoil atomFamily)

type Foo = unknown & { index: string }
type Response = {
  foos: Foo[]
}
const myAtomFamily = useAtomFamily<Foo, string>({
  key: 'foo',
  default: null,
})
declare const fetchFromApi = () => Promise<Foo[]>

So if I have in a custom hook,

const myHook = () => {
  const fooSetter = useSetRecoilState(myAtomFamily(???))

  useEffect(() => {
    fetchFromApi().then(foos => {
      foos.forEach(foo => fooSetter(???)(???))
    })
  }, [])
}

Solution

  • You can you use the useRecoilCallback hook for that, which is basically a factory hook that gives you access to the set function:

    const useMyHook = () => {
      const fetchAndSet = useRecoilCallback(({set}) => async () => {
        const foos = await fetchFromApi();
    
        foos.forEach(foo => set(myAtomFamily(foo.id), foo))
      }, [])
    
      useEffect(fetchAndSet, [])
    }