为什么我的React组件在使用useEffect钩子时没有更新?

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

Why is my React component not updating with useEffect hook?

问题

我正在尝试使用React的useEffect钩子来渲染一个组件(SideNavbar),但该组件(SideNavbar)在我手动重新加载页面之前不会更改。系统在我重新加载页面时可以正常工作,但如何在不手动重新加载页面的情况下实现呢?

我需要在用户未登录时移除SideNavbar并在用户登录时显示导航栏。如何在用户成功登录后立即重新渲染组件?还有,在用户注销时如何隐藏组件(SideNavbar)?

import Navbar from "./Components/Navbar";
import React, { useEffect, useState } from "react";
import { Route, Routes } from "react-router";
import Home from "./Routes/Home";
import Contact from "./Routes/Contact";
import Enroll from "./Routes/Enroll";
import Login from "./Routes/Login";
import SideNavbar from "./Components/InnerComponents/SideNavbar";
import Profile from './Routes/Admin/Profile';
import Notice from './Routes/Admin/Notice';
import Result from './Routes/Admin/Result';
import StudentList from "./Routes/Admin/StudentList";
import TeacherList from "./Routes/Admin/TeacherList";
import Classroom from "./Routes/Admin/Classroom";
import NoticeState from "./context/notices/NoticeState";
import NewAccount from "./Routes/Admin/NewAccount.js";
import UserState from "./context/user/UserState";

function App() {

  const [isLoggedIn, setIsLoggedIn] = useState(false);

  useEffect(() => {
    if (localStorage.getItem('token')) {
      setIsLoggedIn(true);
    }
  }, [localStorage.getItem('token')]);

  return (
    <div className="App">
      <UserState>
        <NoticeState>
          <Navbar/>
          {isLoggedIn && <SideNavbar/>}
          <div className="containerApp">
            <Routes>
              <Route element={<NewAccount/>} exact path='/newaccount' />
              <Route element={<Profile/>} exact path='/profile' />
              <Route element={<Notice/>} exact path="/notice"/>
              <Route element={<Result/>} exact path="/result"/>
              <Route element={<TeacherList/>} exact path="/teacherList" />
              <Route element={<Classroom/>} exact path="/classroom" />
              <Route element={<StudentList/>} path="/classroom/:classID"/>

              <Route element={<Home/>} exact path='/' />
              <Route element={<Contact/>} exact path='/contact' />
              <Route element={<Enroll/>}  exact path='/enroll' />
              <Route element={<Login/>}  exact path='/login' />
            </Routes>
          </div>
        </NoticeState>
      </UserState>
    </div>
  );
}

export default App;

请注意,上面的代码中已经使用localStorage来检查用户是否已登录,并根据登录状态渲染了SideNavbar组件。当用户成功登录或注销时,isLoggedIn状态会发生变化,从而触发重新渲染。如果你有其他问题或需要进一步的帮助,请告诉我。

英文:

I am trying to use useEffect hook of react to render a component(SideNavbar) but the component(SideNavbar) is not changing until i reload the page manually.
The system works when i reload the page but how can i do it without having to manualy reload the page.

I need to remove a Sidenavbar when user is not logged in and display the navbar when user is logged in.
How to make the component rerender as soon as the user has succesfully logged in?
Also How to hide the component (SideNavbar) as soon as the user logs Out.

import Navbar from &quot;./Components/Navbar&quot;;
import React, { useEffect, useState } from &quot;react&quot;;
import { Route,Routes } from &quot;react-router&quot;;
import Home from &quot;./Routes/Home&quot;;
import Contact from &quot;./Routes/Contact&quot;;
import Enroll from &quot;./Routes/Enroll&quot;;
import Login from &quot;./Routes/Login&quot;;
import SideNavbar from &quot;./Components/InnerComponents/SideNavbar&quot;;
import Profile from &#39;./Routes/Admin/Profile&#39;;
import Notice from &#39;./Routes/Admin/Notice&#39;;
import Result from &#39;./Routes/Admin/Result&#39;;
import StudentList from &quot;./Routes/Admin/StudentList&quot;;
import TeacherList from &quot;./Routes/Admin/TeacherList&quot;;
import Classroom from &quot;./Routes/Admin/Classroom&quot;;
import NoticeState from &quot;./context/notices/NoticeState&quot;;
import NewAccount from &quot;./Routes/Admin/NewAccount.js&quot;
import UserState from &quot;./context/user/UserState&quot;;
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
useEffect(()=&gt;{
if(localStorage.getItem(&#39;token&#39;)){
setIsLoggedIn(true);
}
},[localStorage.getItem(&#39;token&#39;)])
return (
&lt;div className=&quot;App&quot;&gt;
&lt;UserState&gt;
&lt;NoticeState&gt;
&lt;Navbar/&gt;
{isLoggedIn &amp;&amp; &lt;SideNavbar/&gt;}
&lt;div className=&quot;containerApp&quot;&gt;
&lt;Routes&gt;
&lt;Route element={&lt;NewAccount/&gt;} exact path=&#39;/newaccount&#39; /&gt;
&lt;Route element={&lt;Profile/&gt;} exact path=&#39;/profile&#39; /&gt;
&lt;Route element={&lt;Notice/&gt;} exact path=&quot;/notice&quot;/&gt;
&lt;Route element={&lt;Result/&gt;} exact path=&quot;/result&quot;/&gt;
&lt;Route element={&lt;TeacherList/&gt;} exact path=&quot;/teacherList&quot; /&gt;
&lt;Route element={&lt;Classroom/&gt;} exact path=&quot;/classroom&quot; /&gt;
&lt;Route element={&lt;StudentList/&gt;} path=&quot;/classroom/:classID&quot;/&gt;
&lt;Route element={&lt;Home/&gt;} exact path=&#39;/&#39; /&gt;
&lt;Route element={&lt;Contact/&gt;} exact path=&#39;/contact&#39; /&gt;
&lt;Route element={&lt;Enroll/&gt;}  exact path=&#39;/enroll&#39; /&gt;
&lt;Route element={&lt;Login/&gt;}  exact path=&#39;/login&#39; /&gt;
&lt;/Routes&gt;
&lt;/div&gt;
&lt;/NoticeState&gt;
&lt;/UserState&gt;
&lt;/div&gt;
);
}
export default App;

答案1

得分: 1

以下是翻译好的部分:

问题出在这个 `useEffect` 

```tsx
useEffect(() => {
    if (localStorage.getItem('token')) {
      setIsLoggedIn(true);
    }
  }, [localStorage.getItem('token')])

因为在依赖数组中,存在 localStorage.getItem('token'),它只会被调用一次,<App/> 没有理由重新渲染(这是由 setState 引起的)。
最快的解决方法是创建一个周期性检查,看看 localStorage 中是否存在令牌。

useEffect(() => {
   const intervalInstance = setInterval(() => {
      if (localStorage.getItem('token')) setIsLoggedIn(true)
      else setIsLoggedIn(false)
   }, 500); // 每500毫秒检查一次

   // 在卸载时移除定时器以防止内存泄漏
   return () => { clearInterval(intervalInstance) }
}, [])

这将起作用,但不是理想的解决方案。以下是一些原因:

  1. 令牌检查 - 仅仅有一个令牌并不意味着您已经登录。应该检查令牌是否有效,是否过期,是否来自正确的发行者等等。有一些更合适的方法来做这个工作:oAuth 指南另一个相当全面的指南。您可以在互联网上找到大量这些指南。
  2. 安全性 - 将令牌存储在 localStorage 中非常方便,但有很多很多弊端。它可能会泄漏,因此不推荐这种方式。您可以在这个 SO 帖子中了解更多信息。
  3. 周期性检查 - 这些远非理想。借助于 React 的响应式系统和自动更新,例如 setStateReact.Context 值的更改,我们应该寻找一种解决方案,即在登录成功并获得令牌后,通过更新 React.Context 值、设置状态等,自动重新渲染 <App/>。关键是,应该在成功登录时触发此重新渲染,通过设置一个值,该值将更新 &lt;App/&gt; 组件。React 将负责重新渲染和显示 Sidebar 等与重新渲染相关的内容。

<details>
<summary>英文:</summary>
The problem is in this `useEffect`
```tsx
useEffect(()=&gt;{
if(localStorage.getItem(&#39;token&#39;)){
setIsLoggedIn(true);
}
},[localStorage.getItem(&#39;token&#39;)])

Because in deps array, there is localStorage.getItem(&#39;token&#39;), which is called once, the &lt;App/&gt; has no reason to rerender (which is caused by setState).
The quickest solution is to create a periodical check if there is a token in the localStorage.

useEffect(() =&gt; {
   const intervalIntance = setInterval(() =&gt; {
      if(localStorage.getItem(&#39;token&#39;)) setIsLoggedIn(true)
      else setIsLoggedIn(false)
   }, 500); // check every 500 ms

   // on unmount remove the interval to prevent memory leaks
   return () =&gt; { clearInterval(intervalInstance) }
},[])

It will work, but it's not an ideal solution. Here are some reasons why:

  1. Token check - just the fact, that you have a token does not mean you are logged in. The token should be checked if it's valid, not outdated, from the correct issuer and so on. There are some guides how to do this more properly: oAuth guide, another quite comprehensive guide. You can find a ton of them on the internet.
  2. Security - storing token in localStorage is convenient, but comes with many, many drawbacks. It can leak easily, so it's not a recommended way. You can read more in this SO post.
  3. Periodical checks - these are far from ideal. Thanks to React reactivity system and automatic updates on e.g. setState or React.Context value changes, we should rather look for a solution, that after logging in and obtaining the token will cause automatic rerender of the &lt;App/&gt; e.g. by updating React.Context value, setting the state. The clue is, that you should trigger this rerender on successful login by setting up a value that will update &lt;App/&gt; component. React will take care of rest related to rerendering and showing Sidebar.

答案2

得分: 0

需要在用户成功登录/注销时重新渲染组件。
使用useEffect依赖项如下所示的方式。

检查这段更新后的代码。

import React, { useEffect, useState } from "react";
import { Route, Routes } from "react-router";
import Home from "./Routes/Home";
import Contact from "./Routes/Contact";
import Enroll from "./Routes/Enroll";
import Login from "./Routes/Login";
import SideNavbar from "./Components/InnerComponents/SideNavbar";
import Profile from './Routes/Admin/Profile';
import Notice from './Routes/Admin/Notice';
import Result from './Routes/Admin/Result';
import StudentList from "./Routes/Admin/StudentList";
import TeacherList from "./Routes/Admin/TeacherList";
import Classroom from "./Routes/Admin/Classroom";
import NoticeState from "./context/notices/NoticeState";
import NewAccount from "./Routes/Admin/NewAccount.js";
import UserState from "./context/user/UserState";

function App() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  useEffect(() => {
    const token = localStorage.getItem('token');
    setIsLoggedIn(!!token);
  }, []);

  const handleLogin = () => {
    setIsLoggedIn(true);
  };

  const handleLogout = () => {
    localStorage.removeItem('token');
    setIsLoggedIn(false);
  };

  return (
    <div className="App">
      <UserState>
        <NoticeState>
          <Routes>
            <Route element={<NewAccount />} exact path="/newaccount" />
            <Route element={<Profile />} exact path="/profile" />
            <Route element={<Notice />} exact path="/notice" />
            <Route element={<Result />} exact path="/result" />
            <Route element={<TeacherList />} exact path="/teacherList" />
            <Route element={<Classroom />} exact path="/classroom" />
            <Route element={<StudentList />} path="/classroom/:classID" />

            <Route element={<Home />} exact path="/" />
            <Route element={<Contact />} exact path="/contact" />
            <Route element={<Enroll />} exact path="/enroll" />
            <Route
              element={<Login onLogin={handleLogin} />}
              exact path="/login"
            />
          </Routes>
          <Navbar />
          {isLoggedIn && <SideNavbar onLogout={handleLogout} />}
        </NoticeState>
      </UserState>
    </div>
  );
}

export default App;
英文:

You need to re-render the component as user successfully logs in / logs out.
Use useEffect dependancy stated as in below code.

Check this updated code.

import React, { useEffect, useState } from &quot;react&quot;;
import { Route, Routes } from &quot;react-router&quot;;
import Home from &quot;./Routes/Home&quot;;
import Contact from &quot;./Routes/Contact&quot;;
import Enroll from &quot;./Routes/Enroll&quot;;
import Login from &quot;./Routes/Login&quot;;
import SideNavbar from &quot;./Components/InnerComponents/SideNavbar&quot;;
import Profile from &#39;./Routes/Admin/Profile&#39;;
import Notice from &#39;./Routes/Admin/Notice&#39;;
import Result from &#39;./Routes/Admin/Result&#39;;
import StudentList from &quot;./Routes/Admin/StudentList&quot;;
import TeacherList from &quot;./Routes/Admin/TeacherList&quot;;
import Classroom from &quot;./Routes/Admin/Classroom&quot;;
import NoticeState from &quot;./context/notices/NoticeState&quot;;
import NewAccount from &quot;./Routes/Admin/NewAccount.js&quot;;
import UserState from &quot;./context/user/UserState&quot;;
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
useEffect(() =&gt; {
const token = localStorage.getItem(&#39;token&#39;);
setIsLoggedIn(!!token);
}, []);
const handleLogin = () =&gt; {
setIsLoggedIn(true);
};
const handleLogout = () =&gt; {
localStorage.removeItem(&#39;token&#39;);
setIsLoggedIn(false);
};
return (
&lt;div className=&quot;App&quot;&gt;
&lt;UserState&gt;
&lt;NoticeState&gt;
&lt;Routes&gt;
&lt;Route element={&lt;NewAccount /&gt;} exact path=&quot;/newaccount&quot; /&gt;
&lt;Route element={&lt;Profile /&gt;} exact path=&quot;/profile&quot; /&gt;
&lt;Route element={&lt;Notice /&gt;} exact path=&quot;/notice&quot; /&gt;
&lt;Route element={&lt;Result /&gt;} exact path=&quot;/result&quot; /&gt;
&lt;Route element={&lt;TeacherList /&gt;} exact path=&quot;/teacherList&quot; /&gt;
&lt;Route element={&lt;Classroom /&gt;} exact path=&quot;/classroom&quot; /&gt;
&lt;Route element={&lt;StudentList /&gt;} path=&quot;/classroom/:classID&quot; /&gt;
&lt;Route element={&lt;Home /&gt;} exact path=&quot;/&quot; /&gt;
&lt;Route element={&lt;Contact /&gt;} exact path=&quot;/contact&quot; /&gt;
&lt;Route element={&lt;Enroll /&gt;} exact path=&quot;/enroll&quot; /&gt;
&lt;Route
element={&lt;Login onLogin={handleLogin} /&gt;}
exact path=&quot;/login&quot;
/&gt;
&lt;/Routes&gt;
&lt;Navbar /&gt;
{isLoggedIn &amp;&amp; &lt;SideNavbar onLogout={handleLogout} /&gt;}
&lt;/NoticeState&gt;
&lt;/UserState&gt;
&lt;/div&gt;
);
}
export default App;

huangapple
  • 本文由 发表于 2023年6月2日 14:03:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76387513.html
匿名

发表评论

匿名网友

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

确定