rehooks / local-storage

React hook which syncs localStorage[key] with the comp.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.

commented

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