Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to listen to localstorage value changes in react?

I want to show a button when user is logged.If user is not logged then I m not showing button.When user logged i will set local storage values.when i set local storage in login Component,Header component must listen to that event and show the button.I m using addEventListener for listening.But its not listening.

I don't know where to listen in header Component.

// HeaderComponent(header.js):

class HeaderComponent extends Component {
    componentDidMount(){
        if(typeof window!='undefined'){
            console.log(localStorage.getItem("token"));
            window.addEventListener("storage",function(e){
               this.setState({ auth: true});
            })
        }
    } 
    render() {

    return (
        <div className="header">
            <div className="container">
                <div className="header-content">
                    <img src={logo} alt="logo"></img>
                    <div className="nav-links" >
                        <ul >
                            <li>Home</li>
                            <li>About</li>
                            <li>Services</li>
                            <li><NavLink activeClassName="active" to="/upload" >Upload</NavLink></li>
                            <li><NavLink activeClassName="active" to="/signup"> Sign Up</NavLink></li>


                           { this.state.auth? <li onClick={this.onLogout}>Logout</li> :null}

                        </ul>
                    </div>
                </div>
            </div>

        </div>
    );
   }  
}

//loginComponent(login.js)

class LoginComponent extends Component {
    constructor(props) {
        super(props);
        this.onSubmit = this.onSubmit.bind(this);
    }
    onSubmit(event) {
        const data = {
            username: document.getElementById('name').value,
            password: document.getElementById('password').value
        }
        axios.post(`http://localhost:4000/user/login`, data).then(res => {
            this.props.history.push("/");
            localStorage.setItem("token",res.data.token);
            localStorage.setItem("auth",true);
        }).catch(err => console.log(err));
    }


    render() {
        return (
            <section class="log-in">
                <div class="card-col">
                    <form>
                        <h3>LOG IN</h3>
                        <div class="form-controls">
                            <input id="name" type="text" placeholder="username" class="input"></input>
                        </div>
                        <div class="form-controls">
                            <input id="password" type="password" placeholder="password" class="input"></input>
                        </div>
                        <button type="submit" onClick={this.onSubmit} class="button" >Log in</button>
                    </form>
                </div>

           </section>

        )
    }

}
like image 576
Karthi Avatar asked Jun 19 '19 04:06

Karthi


People also ask

How do you instantly update state when any changes into the localStorage in React JS?

To instantly update state when any changes into the localStorage in React, we listen to the storage event. React. useEffect(() => { window. addEventListener("storage", () => { setCart(JSON.

Can useEffect listen to localStorage?

useEffect does not listen for localStorage.


3 Answers

The current answers are overlooking a really simple and secure option: window.dispatchEvent.

Where you set your localStorage item, if you dispatch an event at the same time then the eventListener in the same browser tab (no need to open another or mess with state) will also pick it up:

  const handleLocalStorage = () => {
    window.localStorage.setItem("isThisInLocalStorage", "true");
    window.dispatchEvent(new Event("storage"));
  };
  window.addEventListener('storage', () => {
      console.log("change to local storage!");
  }
like image 192
crevulus Avatar answered Oct 06 '22 01:10

crevulus


I found a really bad hack to accomplish this:

I have a Toolbar and a Login Component where the Toolbar component listens to changes in localStorage and displays the logged-in user name when the Login Component updates local storage if authentication is successful.

The Toolbar Component

(similar to the Header component in your case)

const [loggedInName, setLoggedInName] = useState(null);

    useEffect(() => {
        console.log("Toolbar hi from useEffect")
        setLoggedInName(localStorage.getItem('name') || null)
        window.addEventListener('storage', storageEventHandler, false);

    }, []);

    function storageEventHandler() {
        console.log("hi from storageEventHandler")
        setLoggedInName(localStorage.getItem('name') || null)
    }

    function testFunc() {  
        console.log("hi from test function")
        storageEventHandler();
    }

Add a hidden button to your Toolbar component. This hidden button will call the testFunc() function when clicked which will update the logged-in user's name as soon as local storage is updated.

   <button style={{ display: 'none' }} onClick={testFunc} id="hiddenBtn">Hidden Button</button>

Now, in your Login component

   .
   .
   .
   //login was successful, update local storage
   localStorage.setItem("name",someName)


   //now click the hidden button using Javascript
   document.getElementById("hiddenBtn").click();
   .
like image 39
mnagdev Avatar answered Oct 06 '22 01:10

mnagdev


Please take note of two things

  1. storage event works only when the same application opened in two browser tabs (it is used to exchange info between different tabs of the same app). Storage event will not fire when both components shown on the same page.

  2. When adding event listerner, you're passing function(), not array function. function() doe not capture this so you should explicitly bind(this) or change it to arrow function.

    For example

    window.addEventListener("storage",(function(e){
           this.setState({ auth: true});
        }).bind(this));
    

    Or do with arrow function

    window.addEventListener("storage",(e) => {
           this.setState({ auth: true});
        });
    

Here is simple example.

Be sure to open it in two tabs (the same link). Store value in one tab and see this value in another tab.

like image 36
Fyodor Avatar answered Oct 06 '22 03:10

Fyodor