How to make localStorage reactive in react?

Viewed 395

I'm using localStorage to save user data on login. I've saved it named "jwt_data". I've created a function on saperate js file to retrieve that data as

export const auth = () => {
  if(localStorage.getItem('jwt_data')){
    return JSON.parse(localStorage.getItem('jwt_data'))
  }else{
    return false
  }
}

and i've a navbar component on react with < logo , searchbar, a dropdown>. I wanted to hide that once user logout and show once i login . I used code as

const auth_user = auth()
........
<>
{auth_user ? (// if logged in..... )  : (// if user is logged out........)

</>

It did worked. But problem is it is not reactive. I need to reload the website to see that effect.

I also tried to use other such as

const [auth_user, setAuth_user] = useState(false)
useEffect(() => {
 if(auth()) {setAuth_user(true)}    
 else {setAuth_user(false)}
})

Which have same result as previous. I dont know where i'm wrong. Other people suggested to use redux and things, which is too complicated for my mind and my simple application.

My app.js look like this

import React from 'react';
import Nav from './Nav';
import Home from './Home';
import Login from './Login';
import Logout from './Logout';


function App() {
  return (
    <div className="App">
      <Router>
     <Router>
      <Nav/>
      <Routes>
        <Route exact path="/home" element={<Home/>}/>
        <Route exact path="/login" element={<Login/>}/>
        <Route exact path="/logout" element={<Logout/>}/>
      </Routes>
      </Router>
    </div>
  );
}

After I login, im moved to home and while doing so , i wanted to remove all the things in navbar have that is only shown that is authenticated, like in this case and also general other scenario.

Any thing i'm missing here ? Thank you for your kind answer.

2 Answers

So you need useContext in this case.

In your app.js

import React, { useContext } from "react";

export const AuthContext = React.createContext(null);

function App() {

  const [authContext, setAuthContext] = useState(localStorage.getItem("auth"));

  return (
    <AuthContext.Provider value={authContext, setAuthContext}>
      <Router>
        <Nav/>
        <Routes>
          <Route exact path="/home" element={<Home/>}/>
          <Route exact path="/login" element={<Login/>}/>
          <Route exact path="/logout" element={<Logout/>}/>
        </Routes>
      </Router>
    </AuthContext.Provider>
  );
}

and then in your Nav.js

import React, { useContext } from "react";
import { AuthContext } from "./App.js"

function Nav() {

  const [authContext, setAuthContext] = useContext(AuthContext);

  return (
    authContext
      ? /*Navbar with user stuff*/
      : /*Empty Navbar */
  )
}

and in your Login component:

import React, { useContext } from "react";
import { AuthContext } from "./App.js"

function Login() {

  const [authContext, setAuthContext] = useContext(AuthContext);

  const login = () => {
    const jwt = "jwt"; // fetch your jwt
    localStorage.setItem("auth", jwt)
    setAuthContext(jwt);
  }

  return (
    <div>Login form</div>
  )
}

and Logout:

import React, { useContext } from "react";
import { AuthContext } from "./App.js"

function Logout() {

  const [authContext, setAuthContext] = useContext(AuthContext);

  const logout = () => {
    localStorage.setItem("auth", null)
    setAuthContext(null);
  }

  return (
    <button onClick={logout}>Logout button</button>
  )
}

I think the problem here is that you see localStorage as your state, which is not quite right in React space. In order for your component to update you need to keep this information in a state that your component can subscribe to. Either React state (useState) or a global one (redux, context etc).

Then your locale storage key would be a way to initialize this state (e.g on refresh). So when logged in state changes you have to update both of them.

Since you want to keep things simple the best way would be to create a loggedIn state in your App component and then delegate to your children components either the loggedIn state (Nav component) or the setLoggedIn setter (Login/Logout component) to update it.

You can find examples on how to create react hooks that utilize the usage of localeStorage like this one. You can find the code of your App below, if you used the useLocalStorage from the link, or any other similar implementation.

import React from 'react';
import Nav from './Nav';
import Home from './Home';
import Login from './Login';
import Logout from './Logout';
import useLocaleStorage from 'somewhere'


function App() {
  const [loggedIn, setLoggedIn] = useLocalStorage('jwt_data', false);

  return (
    <div className="App">
      <Router>
     <Router>
      <Nav loggedIn={loggedIn} /> // and hide what's need to be hidden
      <Routes>
        <Route exact path="/home" element={<Home/>}/>
        <Route exact path="/login" element={<Login setLoggedIn={setLoggedIn} />}/>
        <Route exact path="/logout" element={<Logout setLoggedIn={setLoggedIn} />}/>
      </Routes>
      </Router>
    </div>
  );
}

However as your App becomes more complicated you may reconsider keeping this in an internal react state and use context instead to avoid prop drilling from your App component to it's children.

Related