Add useMemo call to useLocalStorage so that the value returned only changes if store actually changes.#304
Open
alnorth wants to merge 1 commit intouidotdev:mainfrom
Open
Add useMemo call to useLocalStorage so that the value returned only changes if store actually changes.#304alnorth wants to merge 1 commit intouidotdev:mainfrom
alnorth wants to merge 1 commit intouidotdev:mainfrom
Conversation
…hanges if store actually changes.
|
We ran into the same problem - somewhat similar with a JWT, on which a some other hooks depend. I think data in the localStorage might be some basic values (authorization, language) which is used (directly or derived) in a lot of parts of the application. So this trade-off between memory & CPU should be worth using the |
|
update on my workaround, no issues so far. /**
*
* If active tab updates localStorage:
* - At current tab, localStorage is updated.
* - At current tab, "storage" event is emitted.
* - At current tab, window receives "storage" event.
* - At current tab, state is updated.
*
* If inactive tab updates localStorage:
* - At inactive tab, localStorage is updated.
* - At current tab, window receives "storage" event.
* - At current tab, state is updated.
*
*/
import { useCallback, useEffect, useMemo, useState } from "react";
export function useLocalStorage<T>(key: string) {
const [state, set_state] = useState<T | null>(() => {
const unparsed = localStorage.getItem(key);
if (typeof unparsed === "string") {
const parsed = JSON.parse(unparsed) as T;
return parsed;
}
return null;
});
useEffect(() => {
const listener = (e: StorageEvent) => {
if (e.key === key) {
if (typeof e.newValue === "string") {
const parsed = JSON.parse(e.newValue) as T;
set_state(parsed);
}
}
};
window.addEventListener("storage", listener);
return () => {
window.removeEventListener("storage", listener);
};
}, [key]);
/**
* @description dispatches an event so all instances gets updated.
*/
const set_stored = useCallback(
(value: T | null) => {
const newValue = JSON.stringify(value);
localStorage.setItem(key, newValue);
const event = new StorageEvent("storage", { key, newValue });
dispatchEvent(event);
},
[key],
);
return useMemo(
() => [state, set_stored] as [T | null, (value: T | null) => void],
[state, set_stored],
);
}
export default useLocalStorage;import useLocalStorage from "./useLocalStorage";
export interface Session {
id: string;
iss: string;
aud: string;
sub: string;
iat: number;
nbf: number;
exp: number;
}
export const useSession = () => {
return useLocalStorage<Session>("session");
};
export default useSession; |
|
This hook should accept an initialValue being passed. |
|
@lynnandtonic @tylermcginnis Any chance we can get this merged? |
|
Is this package unmaintained? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

At the moment if a component that uses
useLocalStoragererenders thenuseLocalStoragewill callJSON.parse(store)as a result of the rerender. The value ofstorewon't have changed, but the new call toJSON.parsewill mean that a new value is returned with identical contents to the old value. Any hooks then using that value in their deps array will then have to be run again, even though the actual data is the same.I've updated useLocalStorage so that it uses
useMemoto avoid repeated calls toJSON.parsewith the same store value.