如何在React.JS中在登录/登出时更改导航栏文本?

huangapple go评论84阅读模式
英文:

How to change NavBar text on login/logout in React.JS?

问题

I understand your code, and it seems like you're facing an issue where the NavBar component doesn't update immediately when the login state changes. This is likely because the localStorage value for "token" is only read once during the initial rendering of the App component.

To solve this issue, you can use the useEffect hook to listen for changes in the localStorage value and update the loggedIn state accordingly. Here's how you can modify your App component:

import React, { useState, useEffect } from 'react';

export default function App() {
  const [loggedIn, setLoggedIn] = useState(localStorage.getItem("token"));

  useEffect(() => {
    // Use the localStorage value to update the loggedIn state when it changes
    const token = localStorage.getItem("token");
    setLoggedIn(token);
  }, []);

  return (
    <div>
      <Router>
        <Navbar isAuth={loggedIn} />
        <Route exact path="/" exact component={Home} />
        <Route path="/login" component={Login} />
        <PrivateRoute path="/dashboard" component={Dashboard} />
      </Router>
    </div>
  );
}

By using useEffect with an empty dependency array ([]), you ensure that the loggedIn state is updated whenever the "token" in localStorage changes.

This should make your NavBar component update itself immediately when you log in or log out without the need for a manual page refresh.

英文:

I have a Navigation bar in my project which I call from inside App.js. Based on if I am logged in or not, I want to render different views of NavBar. If logged in, I want the NavBar to have a logout button. And if logged out, I want the NavBar to have login button. I use a token in localStorage to check if I am logged in or not. When logged in, token is present in localStorage. On logout/before login, there is no token key in localStorage. I pass this token as a state to NavBar as shown:

export default function App() {
   const [loggedIn, setLoggedIn] = useState(localStorage.getItem(&quot;token&quot;));
   return (
      &lt;div&gt;
         &lt;Router&gt;
            &lt;Navbar isAuth={loggedIn} /&gt;
            &lt;Route exact path=&quot;/&quot; exact component={Home} /&gt;
            &lt;Route path=&quot;/login&quot; component={Login} /&gt;
            &lt;PrivateRoute path=&quot;/dashboard&quot; component={Dashboard} /&gt;
         &lt;/Router&gt;
      &lt;/div&gt;
   );
} 

Now from NavBar component, I use this prop to render different views of NavBar as shown below:

const NavBar = props =&gt; {
   const classes = useStyles();

   if (props.isAuth !== null) {
      return (
         &lt;div className={classes.root}&gt;
            &lt;AppBar position=&quot;static&quot;&gt;
               &lt;Toolbar&gt;
                  &lt;Typography variant=&quot;h6&quot; className={classes.title}&gt;
                     &lt;Link
                        href=&quot;/&quot;
                        style={{ textDecoration: &quot;none&quot;, color: &quot;white&quot; }}
                     &gt;
                        Timetracker
                     &lt;/Link&gt;
                  &lt;/Typography&gt;
                  &lt;Link href=&quot;/&quot; style={{ color: &quot;white&quot; }}&gt;
                     &lt;Button color=&quot;inherit&quot; onClick={auth.logout}&gt;
                        Logout
                     &lt;/Button&gt;
                  &lt;/Link&gt;
               &lt;/Toolbar&gt;
            &lt;/AppBar&gt;
         &lt;/div&gt;
      );
   } else {
      return (
         &lt;div className={classes.root}&gt;
            &lt;AppBar position=&quot;static&quot;&gt;
               &lt;Toolbar&gt;
                  &lt;Typography variant=&quot;h6&quot; className={classes.title}&gt;
                     &lt;Link
                        href=&quot;/&quot;
                        style={{ textDecoration: &quot;none&quot;, color: &quot;white&quot; }}
                     &gt;
                        Timetracker
                     &lt;/Link&gt;
                  &lt;/Typography&gt;
                  &lt;Link href=&quot;/login&quot; style={{ color: &quot;white&quot; }}&gt;
                     &lt;Button color=&quot;inherit&quot;&gt;Login&lt;/Button&gt;
                  &lt;/Link&gt;
               &lt;/Toolbar&gt;
            &lt;/AppBar&gt;
         &lt;/div&gt;
      );
   }
};

export default NavBar;

The problem is that, the NavBar does not update itself as soon as I login. I have to manually refresh the page in order to render the new NavBar. Similarly on logout too, It does not update itself and updates only on manual refresh. What is the issue and how to solve this?

答案1

得分: 2

我找到了一个简单的解决方案:
使用 componentDidMount()useEffect() 函数,它将在加载 NavBar 页面时自动渲染。
在这个函数内部,使用 setInterval() 函数来持续获取身份验证状态(比如,间隔为 5000 毫秒)。这将持续刷新 NavBar,并立即更改按钮。
我想你需要将身份验证检查放在 NavBar 组件本身,而不是使用 props。我将想要更改的特定按钮放在一个名为 NavBarUser 的单独组件中,将 "login | signup" 更改为 "logout" 并包含用户头像。然后,我将这个组件插入到适当的位置在 NavBar 中。
这是我的代码示例:

import React, { useState, useEffect } from 'react';
import Avatar from './Avatar';
import { BrowserRouter as Router, Link } from 'react-router-dom';

const NavBarUser = () => {
    const [user, setUser] = useState({});
    
    useEffect(() => {
        /*
        使用 setInterval 以便持续刷新页面,以便在用户登出时立即显示 "logout" 按钮,替代 "login"。
        */
        const intervalId = setInterval(() => {
            const userString = localStorage.getItem("user");
            const user = JSON.parse(userString);
            setUser(user);
        }, 5000);

        return () => clearInterval(intervalId);
    }, []);

    const logout = () => {
        localStorage.removeItem("user");
    };

    if (!user) {
        return (
            <div className="navbar-nav ml-auto">
                <Link to="/login" className="nav-item nav-link">Login</Link> <span className="nav-item nav-link">|</span> <Link to="/SignUp" className="nav-item nav-link">Sign Up</Link>
            </div>
        );
    }

    if (user) {
        return (
            <div className="navbar-nav ml-auto">
                <Link to="/" className="nav-item nav-link" onClick={logout}>Logout</Link>
                <Avatar img="/images/Eat-healthy.jpg" />
            </div>
        );
    }
};

export default NavBarUser;

请注意,这段代码是一个 React 组件,用于处理 NavBar 中的用户身份验证状态和按钮切换。

英文:

I found a simple solution:
use a componentDidMount() or useEffect() function which will render automatically upon loading the NavBar page.
Inside this function, use a setInterval() function to continually retrieve the auth status (say, an interval of 5000). This will continually refresh the NavBar, and change the button immediately.
I imagine you would have to put the auth check in the NavBar component itself, instead of using props. I put the specific buttons I wanted to change in a separate component called NavBarUser, which changes 'login | signup' to 'logout' and contains a user avatar. I then inserted this component into the NavBar itself at the appropriate place.
This is what my code looks like:
```
import React, { useState, useEffect } from 'react';
import Avatar from './Avatar';
import { BrowserRouter as Router, Link } from 'react-router-dom';

const NavBarUser = () =&gt; {

    const [user, setUser] = useState({});
    useEffect(() =&gt; {
    { /*
        setInterval was used in order to refresh the page constantly
    in order to have the &quot;logout&quot; button show immediately in place of
    &quot;login&quot;, as soon as user logs out.
    */}
        setInterval(() =&gt; {
            const userString = localStorage.getItem(&quot;user&quot;);
            const user = JSON.parse(userString);
            setUser(user);
            }, [])
    }, 5000);


    const logout = () =&gt; {
        return localStorage.removeItem(&quot;user&quot;);
    }

    if (!user) {
        return (
            &lt;div className=&quot;navbar-nav ml-auto&quot;&gt;
                &lt;Link to=&quot;/login&quot; className=&quot;nav-item nav-link&quot;&gt;Login&lt;/Link&gt; &lt;span 
className=&quot;nav-item nav-link&quot;&gt;|&lt;/span&gt; &lt;Link to=&quot;/SignUp&quot; className=&quot;nav-item nav- 
link&quot;&gt;Sign Up&lt;/Link&gt;
            &lt;/div&gt;
        )
    }
    if (user) {
       return (
         &lt;div className=&quot;navbar-nav ml-auto&quot;&gt;
            &lt;Link to=&quot;/&quot; className=&quot;nav-item nav-link&quot; onClick={logout}&gt;Logout&lt;/Link&gt;
                    &lt;Avatar img=&quot;/images/Eat-healthy.jpg&quot; /&gt;
            &lt;/div&gt;
        )
    }


}


export default NavBarUser;

```

答案2

得分: 0

你需要添加 &lt;Switch&gt; 也是必要的。根据文档:

> 渲染与位置匹配的第一个子 <Route> 或 <Redirect>。
>
> &lt;Switch&gt; 在于它只渲染一条路由。相比之下,每个匹配位置的 &lt;Route&gt; 都会包容性地渲染。

就像下面这样:

&lt;Router&gt;
  &lt;Switch&gt;
     &lt;Navbar isAuth={loggedIn} /&gt;
     &lt;Route exact path=&quot;/&quot; exact component={Home} /&gt;
     &lt;Route path=&quot;/login&quot; component={Login} /&gt;
     &lt;PrivateRoute path=&quot;/dashboard&quot; component={Dashboard} /&gt;
  &lt;/Switch&gt;
&lt;/Router&gt;

详细阅读:Router

希望这有所帮助!

英文:

You need to add &lt;Switch&gt; as well. From the documentation:

> Renders the first child <Route> or <Redirect> that matches the location.
>
> &lt;Switch&gt; is unique in that it renders a route exclusively. In contrast, every &lt;Route&gt; that matches the location renders inclusively.

Just like the following:

&lt;Router&gt;
  &lt;Switch&gt;
     &lt;Navbar isAuth={loggedIn} /&gt;
     &lt;Route exact path=&quot;/&quot; exact component={Home} /&gt;
     &lt;Route path=&quot;/login&quot; component={Login} /&gt;
     &lt;PrivateRoute path=&quot;/dashboard&quot; component={Dashboard} /&gt;
  &lt;/Switch&gt;
&lt;/Router&gt;

Read further here: Router

I hope that helps!

答案3

得分: 0

你的应用程序的状态不会在你更改localStorage中的token的值时更新。

你需要确保更新状态,如果有帮助,我已经添加了一个沙盒

英文:

Your app's state won't update if you change the value of the token in localStorage.

You need to make sure you update the state, I've added a sandbox if it helps.

答案4

得分: 0

以下是你要翻译的内容:

Here's how I solved this issue:

To start, I created a isLoggedIn state in my App class. I gave it a componentDidMount() method that would fetch the login state from a cookie on app start. Then I created globalLogin and globalLogout methods as arrow functions, which set the isLoggedIn state to true or false accordingly. I passed my Nav component the isLoggedIn state as a prop and passed the Login and Nav routes the globalLogin and globalLogout methods. These methods can then be called from Login or Nav with this.props.globalLogout(); or this.props.globalLogin();.

This is a simplified version of my App.js.

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      isLoggedIn: false,
    }
  }
  componentDidMount() {
    const token = Cookie.get("token") ? Cookie.get("token") : null;
    if (token) {
      this.setState({ "isLoggedIn": true });
    }
  }
  globalLogin = () => {
    this.setState({ "isLoggedIn": true });
  }
  globalLogout = () => {
    this.setState({ "isLoggedIn": false });
  }

  render() {
    return (
      <Router>
        <div className="App">
          <Nav isLoggedIn={this.state.isLoggedIn} globalLogout={this.globalLogout}/>
          <Switch>
            <Route path="/" exact component={Home} />
            <Route path="/login" exact>
              <Login globalLogin={this.globalLogin}/>
            </Route>
          </Switch>
        </div>
      </Router>
    );
  }
}

EDIT: using history.push didn't work in login module above so I added an intermediate to handle props

render() {
    const LoginIntermediate = (props) => {
      return (
        <Login {...props} globalLogin={this.globalLogin}/>
      )
    }
    return (
      <Router>
        <div className="App">
          <Nav isLoggedIn={this.state.isLoggedIn} globalLogout={this.globalLogout}/>
          <Switch>
            <Route path="/" exact component={Home} />
            <Route path="/login" exact component={LoginIntermediate} />
          </Switch>
        </div>
      </Router>
    );
  }
}
英文:

Here's how I solved this issue:

To start, I created a isLoggedIn state in my App class. I gave it a componentDidMount() method that would fetch the login state from a cookie on app start. Then I created globalLogin and globalLogout methods as arrow functions, which set the isLoggedIn state to true or false accordingly. I passed my Nav component the isLoggedIn state as a prop and passed the Login and Nav routes the globalLogin and globalLogout methods. These methods can then be called from Login or Nav with this.props.globalLogout(); or this.props.globalLogin();.

This is a simplified version of my App.js.

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      isLoggedIn: false,
    }
  }
  componentDidMount() {
    const token = Cookie.get(&quot;token&quot;) ? Cookie.get(&quot;token&quot;) : null;
    if (token) {
      this.setState({ &quot;isLoggedIn&quot;: true });
    }
  }
  globalLogin = () =&gt; {
    this.setState({ &quot;isLoggedIn&quot;: true });
  }
  globalLogout = () =&gt; {
    this.setState({ &quot;isLoggedIn&quot;: false });
  }

  render() {
    return (
      &lt;Router&gt;
        &lt;div className=&quot;App&quot;&gt;
          &lt;Nav isLoggedIn={this.state.isLoggedIn} globalLogout={this.globalLogout}/&gt;
          &lt;Switch&gt;
            &lt;Route path=&quot;/&quot; exact component={Home} /&gt;
            &lt;Route path=&quot;/login&quot; exact&gt;
              &lt;Login globalLogin={this.globalLogin}/&gt;
            &lt;/Route&gt;
          &lt;/Switch&gt;
        &lt;/div&gt;
      &lt;/Router&gt;
    );
  }
}

EDIT: using history.push didn't work in login module above so I added an intermediate to handle props

render() {
    const LoginIntermediate = (props) =&gt; {
      return (
        &lt;Login {...props} globalLogin={this.globalLogin}/&gt;
      )
    }
    return (
      &lt;Router&gt;
        &lt;div className=&quot;App&quot;&gt;
          &lt;Nav isLoggedIn={this.state.isLoggedIn} globalLogout={this.globalLogout}/&gt;
          &lt;Switch&gt;
            &lt;Route path=&quot;/&quot; exact component={Home} /&gt;
            &lt;Route path=&quot;/login&quot; exact component={LoginIntermediate} /&gt;
          &lt;/Switch&gt;
        &lt;/div&gt;
      &lt;/Router&gt;
    );
  }

</details>



huangapple
  • 本文由 发表于 2020年1月3日 22:20:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/59580153.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定