facebookexperimental / Recoil

Recoil is an experimental state management library for React apps. It provides several capabilities that are difficult to achieve with React alone, while being compatible with the newest features of React.
https://recoiljs.org/
MIT License
19.61k stars 1.18k forks source link

Using hooks within selectors #1382

Open grod220 opened 3 years ago

grod220 commented 3 years ago

At the moment, selectors do not allow for any hooks inside of it. This is unfortunate because most external libraries provide hooks. This means it's quite difficult to fully move your logic into recoil. You have to rely upon boilerplate patterns like creating custom hooks that sync the result of the hook with recoil atoms.

Here's an example of this common pattern: https://github.com/facebookexperimental/Recoil/issues/727#issuecomment-754456755

Anything on the roadmap related to this?

drarmstr commented 3 years ago

Yes, actually. The synchronization library we're working on has a pattern which helps with syncing atoms with values from hooks. See this example This particular example initializes with data from props, but data from hooks could be used as well as bi-directional syncing and subscriptions.

grod220 commented 3 years ago

Awesome to hear you are thinking about this! Just some feedback.

<SyncWithProps spam="SPAM" eggs="EGGS" />
<ReadsAtom atom={atomA} />

I find this pattern to be a bit odd as it moves state logic into a place where UI components are typically expected to be. For example, I think it would be nicer to have something like this:

const value = useRecoilValueAndSync(definedAtom, useCustomHook())

The best would be being able to use a hook within a selector, but I'd understand if there are inherent limitations with that.

drarmstr commented 3 years ago

The intent is to keep the atom-specific sync logic in one place with the atom definition, that's what described what we sync. It is then abstracted with how it is synced with a hook that acts as a bridge between the non-React atoms and React hooks. It shouldn't be interlaced with UI logic, that was just a trivial test example. Hopefully it will be more clear when we get documentation.

fyyhome commented 2 years ago

Hi, I'm trying out Recoil in my project. I found it hard to set an atom state in an async selector, the case just be like:

export const useDataQuery = selector({
  key: 'useDataQuery',
  get: async ({ get }) => {
    const resp = await fetchData(Number(get(teamIDState)));
    return resp;
  },
});

I'm wondering how to set a loading state when the selector running the fetch function, and then I want to use useSetRecoilState but found we can't use the hook in the selector. Is there a way to do loading state update?

olefrank commented 1 year ago

I'm looking for common patterns on this. All examples I can find https://recoiljs.org/docs/guides/asynchronous-data-queries/ abstracts away the fetch queries... In my application I have access to the fetch queries through a hook which returns a "service" object... Is this pattern doable with recoil?

export const getProductsSelector = selector<Product[]>({
  key: 'getProductsSelector',
  get: async ({ get }) => {
    // todo
    const service = useService();
    const calculation = get(calculationFormAtom);
    return service.getProducts(calculation); // returns Promise<Product[]>
  },
});

This results in a eslint error react/rules-of-hooks:

React Hook "useService" is called in function "get" that is neither a React function component nor a custom React Hook function.