admin管理员组

文章数量:1022804

I have a custom hook to manage localStorage like below:

import { useEffect, useState } from 'react'

export default function useLocalStorage(key, initValue) {

    const [state, setState] = useState(() => {
        const value = localStorage.getItem(key);
        if (value !== null) {
            return JSON.parse(value);
        }

        localStorage.setItem(key, JSON.stringify(initValue));
        return initValue;
    })

    useEffect(() => {
        localStorage.setItem(key, state);
    }, [key, state])
    
    return [state, setState];
}

This custom hook is declared to use the same localstorage key for two or more ponents in one screen. e,g.

const [state, setState] = useLocalStorage('key', false);

At this time, since ponents using useLocalStorage want to work according to the state change of localStorage, we try to use the state returned by useLocalStorage as the second parameter of useEffect.

useEffect(() => {
   foobar()
}, [state]);

I expected the foobar() function in useEffect to work when the "state" changes in all ponents that declared this useEffect. However, foobar() only worked for one ponent out of several ponents using this same useEffect.Why this is happening? How do I get it to work the way I want it to?

I have a custom hook to manage localStorage like below:

import { useEffect, useState } from 'react'

export default function useLocalStorage(key, initValue) {

    const [state, setState] = useState(() => {
        const value = localStorage.getItem(key);
        if (value !== null) {
            return JSON.parse(value);
        }

        localStorage.setItem(key, JSON.stringify(initValue));
        return initValue;
    })

    useEffect(() => {
        localStorage.setItem(key, state);
    }, [key, state])
    
    return [state, setState];
}

This custom hook is declared to use the same localstorage key for two or more ponents in one screen. e,g.

const [state, setState] = useLocalStorage('key', false);

At this time, since ponents using useLocalStorage want to work according to the state change of localStorage, we try to use the state returned by useLocalStorage as the second parameter of useEffect.

useEffect(() => {
   foobar()
}, [state]);

I expected the foobar() function in useEffect to work when the "state" changes in all ponents that declared this useEffect. However, foobar() only worked for one ponent out of several ponents using this same useEffect.Why this is happening? How do I get it to work the way I want it to?

Share Improve this question edited May 27, 2022 at 6:50 Youssouf Oumar 46.7k16 gold badges103 silver badges105 bronze badges asked May 26, 2022 at 8:43 HATENAHATENA 431 silver badge5 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 6

You should set up an event listener for storage changes in order to have the updated value. See the code below and the note about storage event:

import { useEffect, useState } from 'react'

export default function useLocalStorage(key, initValue) {
  const [state, setState] = useState(() => {
    const value = localStorage.getItem(key);
    if (value !== null) {
      return JSON.parse(value);
    }

    localStorage.setItem(key, JSON.stringify(initValue));
    window.dispatchEvent(new Event("storage"));
    return initValue;
  });

  useEffect(() => {
    localStorage.setItem(key, state);
    window.dispatchEvent(new Event("storage"));
  }, [key, state]);

  useEffect(() => {
    const listenStorageChange = () => {
      setState(() => {
        const value = localStorage.getItem(key);
        if (value !== null) {
          return JSON.parse(value);
        }

        localStorage.setItem(key, JSON.stringify(initValue));
        window.dispatchEvent(new Event("storage"));
        return initValue;
      });
    };
    window.addEventListener("storage", listenStorageChange);
    return () => window.removeEventListener("storage", listenStorageChange);
  }, []);

  return [state, setState];
}

You might wonder why this dispatchEvent is used. Well here is what MDN says about storage event:

The storage event of the Window interface fires when a storage area (localStorage) has been modified in the context of another document.

This dispatchEvent is needed as we need to listen to changes in the same document as well.

I have a custom hook to manage localStorage like below:

import { useEffect, useState } from 'react'

export default function useLocalStorage(key, initValue) {

    const [state, setState] = useState(() => {
        const value = localStorage.getItem(key);
        if (value !== null) {
            return JSON.parse(value);
        }

        localStorage.setItem(key, JSON.stringify(initValue));
        return initValue;
    })

    useEffect(() => {
        localStorage.setItem(key, state);
    }, [key, state])
    
    return [state, setState];
}

This custom hook is declared to use the same localstorage key for two or more ponents in one screen. e,g.

const [state, setState] = useLocalStorage('key', false);

At this time, since ponents using useLocalStorage want to work according to the state change of localStorage, we try to use the state returned by useLocalStorage as the second parameter of useEffect.

useEffect(() => {
   foobar()
}, [state]);

I expected the foobar() function in useEffect to work when the "state" changes in all ponents that declared this useEffect. However, foobar() only worked for one ponent out of several ponents using this same useEffect.Why this is happening? How do I get it to work the way I want it to?

I have a custom hook to manage localStorage like below:

import { useEffect, useState } from 'react'

export default function useLocalStorage(key, initValue) {

    const [state, setState] = useState(() => {
        const value = localStorage.getItem(key);
        if (value !== null) {
            return JSON.parse(value);
        }

        localStorage.setItem(key, JSON.stringify(initValue));
        return initValue;
    })

    useEffect(() => {
        localStorage.setItem(key, state);
    }, [key, state])
    
    return [state, setState];
}

This custom hook is declared to use the same localstorage key for two or more ponents in one screen. e,g.

const [state, setState] = useLocalStorage('key', false);

At this time, since ponents using useLocalStorage want to work according to the state change of localStorage, we try to use the state returned by useLocalStorage as the second parameter of useEffect.

useEffect(() => {
   foobar()
}, [state]);

I expected the foobar() function in useEffect to work when the "state" changes in all ponents that declared this useEffect. However, foobar() only worked for one ponent out of several ponents using this same useEffect.Why this is happening? How do I get it to work the way I want it to?

Share Improve this question edited May 27, 2022 at 6:50 Youssouf Oumar 46.7k16 gold badges103 silver badges105 bronze badges asked May 26, 2022 at 8:43 HATENAHATENA 431 silver badge5 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 6

You should set up an event listener for storage changes in order to have the updated value. See the code below and the note about storage event:

import { useEffect, useState } from 'react'

export default function useLocalStorage(key, initValue) {
  const [state, setState] = useState(() => {
    const value = localStorage.getItem(key);
    if (value !== null) {
      return JSON.parse(value);
    }

    localStorage.setItem(key, JSON.stringify(initValue));
    window.dispatchEvent(new Event("storage"));
    return initValue;
  });

  useEffect(() => {
    localStorage.setItem(key, state);
    window.dispatchEvent(new Event("storage"));
  }, [key, state]);

  useEffect(() => {
    const listenStorageChange = () => {
      setState(() => {
        const value = localStorage.getItem(key);
        if (value !== null) {
          return JSON.parse(value);
        }

        localStorage.setItem(key, JSON.stringify(initValue));
        window.dispatchEvent(new Event("storage"));
        return initValue;
      });
    };
    window.addEventListener("storage", listenStorageChange);
    return () => window.removeEventListener("storage", listenStorageChange);
  }, []);

  return [state, setState];
}

You might wonder why this dispatchEvent is used. Well here is what MDN says about storage event:

The storage event of the Window interface fires when a storage area (localStorage) has been modified in the context of another document.

This dispatchEvent is needed as we need to listen to changes in the same document as well.

本文标签: