Using dispatch pattern with Recoil
See original GitHub issueHello, I’m trying to create an abstraction layer on top of Recoil for my app. I would like state changes to be triggered by dispatching actions (like in Redux), while keeping the benefits of the atomic model (only updating components that are explicitly subscribed to an atom).
This works fine for atoms, but I can’t figure out how to make it work for atomFamilies.
Let’s say I have an atom, foo
, and I can set its value via an action, "setFoo"
:
import {atom, useSetRecoilState} from "recoil";
const foo = atom({
key: "foo",
default: "Hello, I'm foo"
});
const useDispatch = () => {
const setFoo = useSetRecoilState(foo);
return (action, data) => {
switch(action){
case "setFoo":
setFoo(data);
break;
}
};
};
Now, I can call this useDispatch
hook once at the top level of my app, and pass the dispatch function it returns down (perhaps using Context, since the function itself never changes). When a "setFoo"
action is dispatched, the hook sets the new value of the foo
atom, and any components that are subscribed to foo
will rerender. So far so good.
Now, let’s make foo
an atom family, instead of just an atom:
import {atomFamily, useSetRecoilState} from "recoil";
const foo = atomFamily({
key: "foo",
default: "Hello, I'm foo"
});
const useDispatch = () => {
const setFoo = useSetRecoilState(foo(/* ??? */)); // Atom identifier needs to be known here
return (action, data) => {
switch(action){
case "setFoo":
setFoo(data);
break;
}
};
};
The trouble is, the atom identifier needs to be known at the time when useDispatch
is called. I can’t call useSetRecoilState
inside the body of the dispatcher to dynamically create the setter, since that would break the rules of hooks.
Here’s my proposal:
What if useSetRecoilState
and related hooks were able to accept atom families, instead of just atoms? Then, the setter it returns could take an atom identifier as an argument, and I could do something like this:
import {atomFamily, useSetRecoilState} from "recoil";
const foo = atomFamily({
key: "foo",
default: "Hello, I'm foo"
});
const useDispatch = () => {
const setFoo = useSetRecoilState(foo); // Atom *family* is passed in, instead of an atom
return (action, data) => {
switch(action){
case "setFoo":
setFoo(data.id, data.value); // Setter takes atom identifier as an argument
break;
}
};
};
If not, are there any other ways to implement this pattern?
Issue Analytics
- State:
- Created 2 years ago
- Comments:5 (2 by maintainers)
Top GitHub Comments
Here’s the solution I’m using:
In usage:
By returning a callback that receives
dispatch
as an argument, you read the latest state snapshot, which is handy when doing async work.Thanks @mondaychen. You could also use
useRecoilTransaction_UNSTABLE()
with the reducer pattern.Or you could make a universal setter callback: