So I have a websocket app with a use case that I'm having a hard time wrapping my head around on how recoil would work with it.
I have a collection of records keyed by an ID. Normally, I'd just be able to make a selectorFactory to use and update the state of these records wherever in the view they're needed, but I need to update the state somewhere high in the parent, to initialize these values. I also don't think this can be done in the RecoilRoot initialState callback. The component initializing the values is further down _inside_ the root, not above, so using that callback would be somewhat awkward, I think?
Here's what I need to do, basically:
// in another file
const recordAtomFamily = atomFamily({
id: 'record',
default: param => ({ name: param }),
})
// component body
const root = useRecoilRoot()
// in an effect that makes a socket
socket.onmessage = ({ data }) => {
const command = parseMessage(data)
if (command.type === 'init') {
for (const record of command.records) {
root.set(recordAtomFamily(record.name), record)
}
}
}
Hi @kingdaro. Right now the Atoms&Selectors can only be updated via hooks. Similar issue here: #73.
In the future it _might_ be possible for the websocket to be updating one atom. Then have multiple selectors read from that one atom but only trigger an update if _their_ computed value changes.
Internally, we're working on a mechanism to limit updates when the values have not changed. But, we're planning to really clean up that API before publishing it.
https://github.com/facebookexperimental/Recoil/issues/41#issuecomment-629601674
@kingdaro - Have you considered using useRecoilCallback() for this case? Could you make a callback using that which can set the provided recordAtomFamily atom and then use that callback from your socket effect?
Completely forgot about useRecoilCallback. What a gem.
const recordAtomFamily = atomFamily({
id: "record",
default: (param) => ({ name: param }),
});
function Socket() {
const updateRecord = useRecoilCallback(({ set }, record) => {
set(recordAtomFamily(record.name), record);
});
useEffect(() => {
socket.onmessage = ({ data }) => {
const command = parseMessage(data);
if (command.type === "init") {
for (const record of command.records) {
updateRecord(record);
}
}
};
}, [updateRecord]);
return null;
}
I didn't even realize that that's what useRecoilCallback was for 馃槄 Though this usage wasn't documented (at time of writing); I had to look at the source code after seeing the replies to this issue. Either way, I think I can consider this closed
Most helpful comment
Completely forgot about
useRecoilCallback. What a gem.