New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
onSet() handler in effect not triggered when atom initialized via RecoilRoot initializeState or snapshot #767
Comments
@abacaj - I added a unit test for the interaction of atom effects and |
@drarmstr thanks for the quick reply. Please see example here: I am appending to the DOM every time the effect is supposed to trigger:
There is only two times that it is appended. Any time you click the button, it should be appending to the DOM - unless effects are supposed to function that way and I misunderstand them? The only time it works is when we remove |
Got it, so the effect is executing properly, but the issue is the |
any solutions? |
I found a temporary solution, which is to skip the This is really not ideal, but it does the trick for me, until the problem has been fixed. Here's my Recoil Provider: import localForage from 'localforage';
import React, { ReactNode, useEffect } from 'react';
import { RecoilRoot, useSetRecoilState } from 'recoil';
import { useLoading } from 'utils/hooks/loading';
import Loading from 'components/UI/loading/loading';
/** Recoil Provider and persistor */
const RecoilProvider = ({ children }: { children: ReactNode }) => {
return (
<RecoilRoot >
<RecoilPersist>{children}</RecoilPersist>
</RecoilRoot>
);
};
export default RecoilProvider;
const RecoilPersist = ({ children }: { children: ReactNode }) => {
const setAtom1 = useSetRecoilState(atom1);
const setAtom2 = useSetRecoilState(atom2);
const [isLoading, setIsLoading] = useState<boolean>(true);
const persistedAtoms = [
{
key: 'atom1',
setter: setAtom1,
},
{
key: 'atom2',
setter: setAtom2,
},
];
const loadPersistedAtoms = () => {
const loadPersisted = async () => {
try {
for await (const atom of persistedAtoms) {
const persistedData = await localForage.getItem<any>(atom.key);
atom.setter(JSON.parse(persistedData));
}
setIsLoading(false);
} catch (err) {
setIsLoading(false);
return;
}
};
loadPersisted();
};
useEffect(loadPersistedAtoms, []);
return <>{!isLoading ? children : <Loading />}</>;
}; FYI @kwoktung |
A workaround for now could also be to just initialize the atom state in the effect and avoid using the |
@drarmstr That sounds better. Would that be using the Awesome work guys 💪 |
@drarmstr Sorry, never mind I figure out by following the docs 🤷 ... I had the issue that I needed to await to get the value from localForage indexedDB, so I ended up wrapping it in a async function. Here's how it ended import localForage from 'localforage';
import { AtomEffect, DefaultValue } from 'recoil';
/** Check if there's an initial value persisted and load it on set */
const loadPersisted = async <T>({ key, setSelf }: { key: string; setSelf: Parameters<AtomEffect<T>>['0']['setSelf'] }) => {
const savedValue = await localForage.getItem<string>(key);
if (savedValue != null) {
setSelf(JSON.parse(savedValue));
}
};
/**
* Localstorage Atom Effect
*
* Add to `effects_UNSTABLE` to persist atom.
* Side-effect for Atom Manipulating
* @see https://recoiljs.org/docs/guides/atom-effects/
*/
export const persistAtomEffect = <T>(key: string): AtomEffect<T> => ({ setSelf, onSet }) => {
loadPersisted({ key, setSelf });
onSet(async (newValue) => {
if (newValue instanceof DefaultValue) {
localForage.removeItem(key);
} else {
localForage.setItem(key, JSON.stringify(newValue));
}
});
}; Thanks :) |
Note that Atom Effect functions are themselves not const loadPersisted = <T>({ key, setSelf }) => {
setSelf(localForage.getItem(key).then(JSON.parse));
}; |
@drarmstr Amazing, I was wondering how to approach this properly. |
Hii, any update of |
Yep, provided workarounds won't work for example with SSR, where I want to initialize with |
@alexunix - You should be able to initialize state in the atom effect with a synchronous |
Hey! Thanks for reply @drarmstr . I'm using cookies from request header to set Atoms state on initial Server-side render. My problem is probably framework-specific since nextjs framework gets ServerSide parameters and passes them to components like this Would've been perfect combo to have peristent state in cookies if only Added example here. |
Bump! I'm in the same boat as @Noo8lord - I have a NextJS SSR app in production which relies on session tokens to understand who the user is, fetch data on the server, and initialize recoil state in recoil root. Was hoping that I could initialize atoms from the server as I've been doing, and then use the As mentioned somewhere above, the workaround of enabling effects by dropping Appreciate any help / follow up and happy to provide more info. |
same problem as @Noo8lord and @DRoghanian . in the end I managed to fix the issue by using a combination of @mthines and using a singleton service to pass server cookies. Yeah, I set server cookies (req.headers.cookie) to a singleton service so my atom can read from this service. Ugly as f, but until InitializeState works as expected (if it will ever work...) or.. workarounds. |
Should be fixed with #1511 and tested with #1519 for 0.6 release. Note you'll also be able to initialize atoms using props with the upcoming |
Is this issue fixed? I've got an atomEffect which tracks the "history" of an atom by pushing into an array each (I'm using Recoil 0.7.5) |
RecoilRoot using initializeState:
The Atom using an effect:
The function: syncDomainStorage is only triggered on initial render when I use
initializeState
, only if I removeinitializeState
the side-effect function is called when the atom is updated.Any updates to
domainState
atom should be triggering the effect, which is not the behavior observed when used along withRecoilRoot initializeState
The text was updated successfully, but these errors were encountered: