Replies: 7 comments 18 replies
-
So I was able to resolve this by creating a simple atom that grabs the user value directly out of local storage without negatively impacting other functionality. But it would be great if atoms hydrated on first render... I'm only a day into Jotai and am already a big fan. |
Beta Was this translation helpful? Give feedback.
-
The behavior is changed in v2 due to SSR complaining on hydration. Seems like React recommends two renders for SSR. We could create another util for reading localStorage in sync, but it's rather trivial to create a such atom. And, you have more control explicitly. jotai/docs/guides/persistence.mdx Lines 14 to 22 in dae4418 For AsyncStorage, hm, yeah, it's not that easy. |
Beta Was this translation helpful? Give feedback.
-
Thanks for an interesting library! I found it confusing when trying out atomWithStorage and atomWithHash, that the initialValue is used instead of the stored value in the first render. I wrote my own like this, and then found this thread with similar solutions. Since my first render called the event function atomWithStorage(key, initialValue) {
const _local = atom(localStorage.hasOwnProperty(key) ? localStorage[key] : initialValue);
return atom(
(get) => get(_local),
(get, set, newValue) => {
localStorage[key] = newValue;
set(_local, newValue);
}
);
} |
Beta Was this translation helpful? Give feedback.
-
import { atomWithStorage, createJSONStorage } from 'jotai/utils'; const storage = createJSONStorage(() => EncryptedStorage); /**
|
Beta Was this translation helpful? Give feedback.
-
Nice solutions about, but I also want to have localstorage changes listen feature (so if locastorage changed in one tab, atoms in other tabs will get known): import { atom, WritableAtom } from "jotai";
import {
createJSONStorage,
RESET,
unstable_NO_STORAGE_VALUE as NO_STORAGE_VALUE,
} from "jotai/utils";
type Unsubscribe = () => void;
type SetStateActionWithReset<Value> =
| Value
| typeof RESET
| ((prev: Value) => Value | typeof RESET);
export function atomWithWebStorage<Value>(
key: string,
initialValue: Value
): WritableAtom<Value, [SetStateActionWithReset<Value>], void> {
const storage = createJSONStorage(() => window.localStorage);
const valueFromStorage = storage.getItem(key);
const baseAtom = atom<Value>(
valueFromStorage === NO_STORAGE_VALUE ? initialValue : valueFromStorage
);
if (import.meta.env.MODE !== "production") {
baseAtom.debugPrivate = true;
}
baseAtom.onMount = (setAtom) => {
const value = storage.getItem(key);
setAtom(value === NO_STORAGE_VALUE ? initialValue : value);
let unsub: Unsubscribe | undefined;
if (storage.subscribe) {
unsub = storage.subscribe(key, setAtom);
}
return unsub;
};
const anAtom = atom(
(get) => get(baseAtom),
(get, set, update: SetStateActionWithReset<Value>) => {
const nextValue =
typeof update === "function"
? (update as (prev: Value) => Value | typeof RESET)(get(baseAtom))
: update;
if (nextValue === RESET) {
set(baseAtom, initialValue);
return storage.removeItem(key);
}
set(baseAtom, nextValue);
return storage.setItem(key, nextValue);
}
);
return anAtom;
} It is basically an adoption of what jotai already offers(I copy-pasted code), but with this change: const baseAtom = atom<Value>(
valueFromStorage === NO_STORAGE_VALUE ? initialValue : valueFromStorage
); |
Beta Was this translation helpful? Give feedback.
-
The above implementation did not work for me, seems like Here is working TS version of Using export function atomWithWebStorage<Value>(
key: string,
initialValue: Value,
storage = localStorage
) {
const storedValue = storage.getItem(key);
const isString = typeof initialValue === 'string';
const storageValue = storedValue
? isString
? storedValue
: storedValue === 'true'
: undefined;
const baseAtom = atom(storageValue ?? initialValue);
return atom(
get => get(baseAtom) as Value,
(_get, set, nextValue: Value) => {
set(baseAtom, nextValue);
storage.setItem(key, nextValue!.toString());
}
);
} |
Beta Was this translation helpful? Give feedback.
-
@cveering this problem can be resolved by using the export const userAtom = atomWithStorage('user', null, undefined, { getOnInit: true }) |
Beta Was this translation helpful? Give feedback.
-
Hi,
Looking at the console output for the following example code you can see that the darkModeAtom that was initialized with atomWithStorage prints to the console twice on refresh. The first time is the default value. The second time is the recalled (hydrated) value pulled from local storage. That is fine for the darkmode use case, but it won't work for authentication checking.
https://codesandbox.io/s/jotai-persistence-vuwi7?from-embed=&file=/src/app.js
I want to store a userAtom in local storage and check for the existence of the user to determine if access to private routes should be granted.
store.js
PrivateRoutesWrapper.jsx
The problem is that on first render, user is always an empty string so it always redirects to login.
Redux with Redux Persist can handle this, but I like Jotai much better. I could always pop the value directly out of local storage, but then all of the other atomic code that relies on user still falls down and things go sideways quickly. Any suggestions for workarounds?
Thanks!
Beta Was this translation helpful? Give feedback.
All reactions