I can't use this hook without modifications
jarlah opened this issue · comments
I have copied it and changed it a bit. I need to be able to avoid referencing localStorage keys when deleting. And thus I thought that a remove function would be a plausible third item in the return array for the hook.
I see why you made the event listener stuff. If I have two sibling components that both uses the same localStorage entry, then those two components are kept in sync, if I understand correctly? Its not a guaranteed sync, because everything can happen, but its a simple choice when you are not using any state management.
Could we maybe incorporate my suggestion below into this library? Adding a remove function as the third entry in the return array?
Yes, I could make a custom hook, based on this hook. But that's a hack.
import * as React from 'react';
import ReactDOM from 'react-dom';
import {useEffect, useState} from "react";
function App() {
const [test, setTest, removeTest] = useLocalStorage("test")
return (
<>
<span>{test}</span>
<input onChange={(e) => setTest(e.target.value)} value={test || ''} />
<button onClick={removeTest}>Remove</button>
</>
)
}
interface KVP<K, V> {
key: K,
value: V
}
class LocalStorageChanged extends CustomEvent<KVP<string, string | null>> {
static eventName = 'onLocalStorageChange';
constructor(payload: KVP<string, string | null>) {
super(LocalStorageChanged.eventName, { detail: payload });
}
}
function useLocalStorage(key: string, initialValue?: string): [string | null, (value: string) => void, () => void] {
const [storedValue, setStoredValue] = useState<string | null>(() => {
const item = window.localStorage.getItem(key);
return item ? item : (initialValue || null);
});
function onLocalStorageChange(event: LocalStorageChanged | StorageEvent) {
if (event instanceof LocalStorageChanged) {
if (event.detail.key === key) {
setStoredValue(event.detail.value);
}
} else {
if (event.key === key) {
setStoredValue(event.newValue);
}
}
}
useEffect(() => {
const listener = (e: any) => onLocalStorageChange(e as LocalStorageChanged);
window.addEventListener(LocalStorageChanged.eventName, listener);
window.addEventListener('storage', listener);
return () => {
window.removeEventListener(LocalStorageChanged.eventName, listener);
window.removeEventListener('storage', listener);
};
}, []);
const setValue = (value: string | ((s: string | null) => string)) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, valueToStore);
window.dispatchEvent(new LocalStorageChanged({ key, value: valueToStore }));
} catch (error) {
console.log(error);
}
};
const removeValue = () => {
setStoredValue(null);
window.localStorage.removeItem(key);
window.dispatchEvent(new LocalStorageChanged({ key, value: null }));
};
return [storedValue, setValue, removeValue];
}
ReactDOM.render(<App />, document.getElementById('root'));
As mentioned, I could have done:
function useLocalStorageWithRemove(key: string, initialValue?: string): [string | null, (value: string) => void, () => void] {
const [storedValue, setStoredValue] = useLocalStorage(key, initialValue);
return [storedValue, setStoredValue, () => deleteFromStorage(key)]
}
but it doesn't feel right. I don't want to maintain this code.
If you'd like I could make a pull request, with a fix we can agree on, if possible.
added pr #13
I agree, I think having that delete option seems like a good idea. Thanks for bringing this up and making the pr, I just took a look and I'll probably approve it either tomorrow or tonight. I suppose @iamsolankiamit should look at this as well.
Thanks for the PR, it is released under 1.4.0