Handle localStorage changes in React to re-render components

Viewed 6950

My application calls a 3rd party api/function which changes a localStorage variable isUserLoggedIn.

Is it possible to re-render the component on localStorage change? My application renders based on the value of localStorage.getItem('isUserLoggedIn')

<div>
        {
        localStorage.getItem('isUserLoggedIn') ?
        <GlobalHeader
          uniqueid="GlobalHeader"            
          signoutclickhandler={this.signOutHandler}
        />
        :
        <GlobalHeader
          uniqueid="GlobalHeader"    
          signinclickhandler={this.signInHandler}
        />
        }
      </div>
3 Answers

My case is:

I have a function, that works with a backend

function callApi() {
  try {

} catch() {
    clearToken(); // custom func for clearing tokens
   // here I need to throw out the user when token is cleared
}
}

I wanted to use the "useEffect" hook for throwing out

    export function App () {
       useEffect(() => {
         if (!tokenFromLocalStorage) {
            history.push('/login'); // react router history. url changing.
            // I could not do this in the callApi func, because it would be
           // neccessary to pass 'history' to the callApi as param on each calling
          }
        }, [tokenFromLocalStorage]);
     
    return (<div></div>)
    }
  

But the problem is that the useEffect does not call because the local storage changing does not rerender an application.

To solve this problem:

export function App () {
       useEffect(() => {
         if (!tokenFromLocalStorage) {
            history.push('/login');
          }
        }, [tokenFromLocalStorage]);

    return (<div></div>)
    }



function callApi() {
  try {
   ...
} catch() {
    clearToken();

    window.location.reload(); // throw out on the 'login' page if there are no tokens.
            // forced reload needed because changing local storage
            // does not rerender application
  }
}

For now, the useEffect will work and will kick the user out.

You can dispatch an action when changing the localstorage. A better approach will be to create custom hooks that will do the work or create function like

function setLocalStorageItem(key,value) {
     dispatch(...);
     localStorage.setItem(key) = value;
}
function removeLocalStorageItem(key) {
     dispatch(...);
     localStorage.removeItem(key);
}

You can simply listen to the storage event on the window object: the event is fired whenever localStorage is modified. There is no need to force a re-render, when you simply update a component state from the storage event:

const [isUserLoggedIn, setIsUserLoggedIn] = useState(localStorage.getItem('isUserLoggedIn'));

useEffect(() => {
    const onStorage = () => {
        setIsUserLoggedIn = localStorage.getItem('isUserLoggedIn');
    };

    window.addEventListener('storage', onStorage);

    return () => {
        window.removeEventListener('storage', onStorage);
    };
}, []);

return (
    <div>
        {
        isUserLoggedIn ?
        <GlobalHeader
            uniqueid="GlobalHeader"            
            signoutclickhandler={this.signOutHandler}
        />
        :
        <GlobalHeader
            uniqueid="GlobalHeader"    
            signinclickhandler={this.signInHandler}
        />
        }
    </div>
)
Related