如何在 react 中监听 localstorage 值的变化?

Kar*_*thi 7 events addeventlistener local-storage reactjs

我想在用户登录时显示一个按钮。如果用户未登录,则我不显示按钮。当用户登录时,我将设置本地存储值。当我在登录组件中设置本地存储时,标题组件必须侦听该事件,并且显示按钮。我正在使用 addEventListener 进行监听。但它没有在监听。

我不知道在 header 组件中在哪里听。

// 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>
    );
   }  
}
Run Code Online (Sandbox Code Playgroud)

//登录组件(登录.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>

        )
    }

}
Run Code Online (Sandbox Code Playgroud)

cre*_*lus 80

当前的答案忽略了一个非常简单且安全的选项:window.dispatchEvent.

在您设置localStorage项目的地方,如果您同时调度一个事件,那么eventListener在同一浏览器选项卡中(无需打开另一个或弄乱状态)也会拾取它:

const handleLocalStorage = () => {
    window.localStorage.setItem("isThisInLocalStorage", "true");
    window.dispatchEvent(new Event("storage"));
};
Run Code Online (Sandbox Code Playgroud)
window.addEventListener('storage', () => {
    console.log("Change to local storage!");
    // ...
})
Run Code Online (Sandbox Code Playgroud)

编辑:

因为这似乎很有帮助,所以我还建议查看usehooks-tsuseLocalStorage团队的钩子。您不需要将其作为软件包安装;你可以直接复制钩子批发。这个钩子利用了我最初共享的解决方案,但添加了很多更复杂的逻辑。

  • 这个答案对我有用 (8认同)
  • 你不知道这对我有什么帮助。多谢。您确实保存了我的应用程序(或者至少阻止了页面重新加载)。谢谢! (4认同)
  • 这个答案应该会得到数千人的支持。非常有用的信息。谢谢。 (4认同)

Fyo*_*dor 11

请注意两点

  1. storage事件仅在同一个应用程序在两个浏览器选项卡中打开时才起作用(它用于在同一应用程序的不同选项卡之间交换信息)。当两个组件显示在同一页面上时,不会触发存储事件。

  2. 添加事件侦听器时,您传递的是function(),而不是数组函数。function()不捕获,this因此您应该明确bind(this)或将其更改为箭头函数。

    例如

    window.addEventListener("storage",(function(e){
           this.setState({ auth: true});
        }).bind(this));
    
    Run Code Online (Sandbox Code Playgroud)

    或者用箭头函数做

    window.addEventListener("storage",(e) => {
           this.setState({ auth: true});
        });
    
    Run Code Online (Sandbox Code Playgroud)

这是一个简单的例子

请务必在两个选项卡(同一链接)中打开它。将值存储在一个选项卡中并在另一个选项卡中查看此值。

  • 当我在两个选项卡中打开时它工作正常。但我希望它在单个选项卡中监听 (2认同)
  • 在单个选项卡中打开时,存储事件不会触发。存储事件的目标是在选项卡之间交换信息。要在单页面上的组件之间交换信息,请使用 lift state up https://reactjs.org/docs/lifting-state-up.html。所以 `this.state.auth` 将位于 `LoginComponent` 和 `HeaderComponent` 的父组件中,并将作为 props 向下传递 (2认同)