So I'm trying recoilJS for a js game that I am building and it pretty neat, but the need to update atoms from components only feels like a limitation.
To create a game loop, I put all the logic on empty component so I will be able to read and write states. Even if I will construct the logic outside of the component, I will need especially move different stats around all the time. There is a way to update atoms outside of react component (not via hooks)?
it would be great,
and to subscribe outside of component would be nice
related: what would the idiom be for creating async selectors which also subscribe to an external data source and trigger component renders when the (external) data changes?
contrived: a selector which reports the window dimensions and listens via window.onresize etc.
not too contrived (my real-world case): a selector which returns a field from a firestore document, the contents of which might change externally (notified by onSnapshotChanged). This second case is really cool for recoil because the fetched document would be shared state (e.g. key === document.id) so you'd be pretty sure not to unneccessarily re-fetch data...
I'm sure I'm just dim, but it wasn't clear how you'd construct the selector getter in those cases?
@ehahn9 you would probably need to create a hook that when given a field it does a useRecoilValue on an atomFamily (that you presumably map through a selector to make it ReadOnly) which is spawned from the field requested with the default value being the result of fetching the field. You would then need to create a subscriber that you can pass the setter function to via useRecoilCallback that will listen for a change and set the atom value. This would mean that after the very initial creation it will always fetch the current value and the subscriber will keep it up-to-date.
That's my guess anyways.
@Shmew that sounds like an excellent direction. Thanks for the pointer. In this approach, any thoughts on how the hook would know when to unsubscribe from it's listener? I didn't see a way for useRecoilCallback to provide a cleanup to its effect so I'd worry that the hook would keep listening for changes and updating the atom long after all the consumers were unmounted? Apologies if I'm not following fully.
Yeah, you would need to implement some sort of mechanism to cancel the subscriber. Probably via a clean-up useEffect in the component.
@davidmccabe, I'm finding numerous issues requesting access to atoms and selectors outside of components, but issues on the matter get closed without a clear path forward / example.
Like @atulmy authentication inquiry, I cannot figure out how to migrate our authentication logic from Redux to Recoil. We need to call authentication helper functions in our auth library from various components whereby the Recoil state gets updated upon network call completion in the auth library. After a few days of work migrating from Redux to Recoil, I've completely stalled. I am invested and really want Recoil to work.
If the answer is, "Recoil is not an end-to-end state manager", that's fine but need to get clarity on where to and where not to use Recoil.
Thanks for any thoughts / examples on this.
I did some workaround that I created a log components that listen to custom events and update the looks atom, so I can call the fiction that fire the events and send looks from everywhere.
You can do something similar untill a solution will be found
Yes, it's perfectly reasonable to create a React component that has an effect to listen to async changes and update Recoil state in response. Here's an example for listening to changes outside of components and updating Recoil state. Note that the Recoil state isn't "global", but coupled to the <RecoilRoot> that this <Subscriber> component is mounted in; this is important for Concurrent Mode support.
function Subscriber() {
const setState = useSetRecoilState(myState);
useEffect(() => {
const subscriber = setupSubscription();
subscriber.onEvent(value => {
setState(value);
});
return () => subscriber.unsubscribe();
});
return null;
}
or check out this Read/Write example. We are working on improving the API to avoid needing to manually maintain subscriber components, but you should get the general idea.
So combine that with atom family and you can get a semi dynamic state management lib despite hooks' hoops.
Updated documentation for syncing Recoil state with external storage in #680
Most helpful comment
Yes, it's perfectly reasonable to create a React component that has an effect to listen to async changes and update Recoil state in response. Here's an example for listening to changes outside of components and updating Recoil state. Note that the Recoil state isn't "global", but coupled to the
<RecoilRoot>that this<Subscriber>component is mounted in; this is important for Concurrent Mode support.or check out this Read/Write example. We are working on improving the API to avoid needing to manually maintain subscriber components, but you should get the general idea.