【问题标题】:Why my component doesn't re-render itself even tho I used useEffect?(React.js)为什么即使我使用了 useEffect 我的组件也不会重新渲染自己?(React.js)
【发布时间】:2021-10-28 06:50:29
【问题描述】:

最近,我正在制作我的 web 应用程序,但我正在努力解决一个小问题(对我来说并不小)

下面是我的 app.js,我想通过登录/退出状态有条件地渲染 NAV 组件。 (你可以看到它在 Switch 外面!)

<>

      <Nav indexStatus={indexStatus} setIndexStatus={setIndexStatus} />
      
      <Index indexStatus={indexStatus} setIndexStatus={setIndexStatus} />

      <Switch>
        <Route exact path="/" component={Auth(LandingPage,null)}/>
        <Route exact path="/login" component={Auth(LoginPage,false)}/>
        <Route exact path="/calendar" component={Auth(Calendar,true)} />
        <Route exact path="/about" component={Auth(About,false)} />
        <Route exact path="/register" component={Auth(RegisterPage,false)}/>
      </Switch>
      
    </>

为了检查登录状态,我在用户登录/退出时为 localStorage 提供了 true/false 值

登录:

dispatch(loginUser(body))
    .then(response=>{
      if(response.payload.loginSuccess){
        localStorage.setItem('user',response.payload.loginSuccess)
        props.history.push("/")
       console.log(props.parentNode)
       
       
      }else{
        alert('login failed')
      }
    })

退出:

 axios.get("/api/users/logout")
    .then(res=>{
      if(res.data.success){
        console.log(res)
       history.push('/login')
       localStorage.setItem('user','false')

        
    }else{
        alert("Logout failed")

    }
  })
}

我给了 localStorage 值,如下所示:

const [loginStatus,setLoginStatus] =useState(localStorage.getItem('user'))

我用 console.log 检查它,它工作正常

所以我使用 useEffect 来更改 loginStatus 值,并仅重新渲染 localstorage 已更改

const checkLoginStatus= localStorage.user
  useEffect(()=>{
    
    if(checkLoginStatus==='true'){
      setLoginStatus(true)
    }else{
      setLoginStatus(false)
    }
  },[checkLoginStatus])
  console.log(localStorage.user)

最后,我用 ?: 运算符编写了代码,如下所示

return (
    <>
    {loginStatus ===true ? (
      <div>
      <div className="nav">
        <h2>TITLE</h2>
        <div className="buttons">
          <div>About</div>
          <div>How to use</div>
          <button onClick={logoutHandler}>Logout</button>

        <StyledLink to="/login"><FontAwesomeIcon icon={faSignInAlt}/></StyledLink>
        
        
          <button onClick={()=>setIndexStatus(!indexStatus)} >Index</button>
        </div>
      </div>
    </div>)

    
    :
     ( <div>
      <div className="nav">
        <h2>TITLE</h2>
        <div className="buttons">
          <div>About</div>
          <div>How to use</div>
          <button onClick={logoutHandler}>Logout</button>

        <StyledLink to="/login"><FontAwesomeIcon icon={faSignInAlt}/></StyledLink>
        <StyledLink to="/register"><FontAwesomeIcon icon={faUserPlus}/></StyledLink>
        
          <button onClick={()=>setIndexStatus(!indexStatus)} >Index</button>
        </div>
      </div>
    </div>
    )}
    </>

    
    
 
  
   
   );
     }

export default Nav

但是,问题是它只能部分工作,我必须刷新页面才能工作组件条件渲染。我不确定为什么会这样,即使我使用 useEffect 和第二个参数 [checkLoginStatus]

你能告诉我为什么不能正常工作吗?我的逻辑怎么错了? 感谢阅读,感谢您的帮助

【问题讨论】:

  • checkLoginStatus 什么时候更新?如果您正在考虑 localStorage.user 何时更改,那么我认为这是您错的地方。
  • 本地存储非常被动。您基本上可以设置/获取值。您问题中的代码是否都是一个组件?您应该使用 React 状态来跟踪当前的身份验证状态,从本地存储初始化并持久化到本地存储。本地存储并不意味着 状态。您仍然需要更新 React 状态和/或 props 以触发 React 重新渲染组件并再次从 localStorage 读取以获取当前值。
  • @DrewReese 感谢您的具体回答。那么你能推荐我一种更新状态的方法吗?我在编码方面很新,我不知道该怎么做 tbh
  • 当然,没问题。我觉得我需要先看一个完整的代码示例,因为不清楚这些代码 sn-ps 相对于彼此的位置。据我所知,您可以将loginStatus 状态移动到组件中,渲染路由和Nav 组件并传递状态并作为道具更新给需要它们的组件。 Auth HOC 在做什么?
  • useEffect 帮不了你。您应该使用StorageEvent 订阅更改,因为本地存储是被动的(您可以读取或写入)

标签: javascript reactjs


【解决方案1】:

您需要将loginStatus 状态提升到App 父组件,以便可以将状态和更新函数传递给子组件以进行读取和/或更新。

应用程序

loginStatus 状态移动到这个父组件。将loginStatus 传递给Nav 组件以使其处理其条件渲染,并将setLoginStatus 传递给Login 组件以使其更新状态。请注意,路由已重新排序,因此它们不再需要在任何地方指定 exact 属性,它们没有被 Auth HOC 包裹,因为这可能会在每个渲染周期创建一个新组件,现在 Login 组件是渲染在 render 道具上,这样我们就可以加入额外的道具。

const [loginStatus, setLoginStatus] = useState(
  () => !!JSON.parse(localStorage.getItem("user")),
);

return (
  <>
    <Nav
      indexStatus={indexStatus}
      loginStatus={loginStatus} // <-- pass login status
      setIndexStatus={setIndexStatus}
      setLoginStatus={setLoginStatus} // <-- pass updater
    />
  
    <Index indexStatus={indexStatus} setIndexStatus={setIndexStatus} />

    <Switch>
      <Route
        path="/login"
        render={props => <LoginPage {...props} setLoginStatus={setLoginStatus} />}
      />
      <Route path="/calendar" component={Calendar} />
      <Route path="/about" component={About} />
      <Route path="/register" component={RegisterPage} />
      <Route path="/" component={LandingPage} />
    </Switch>
  </>
);

导航

移除loginStatus 状态并使用传递的loginStatus 值和setLoginStatus 函数更新父级中的状态。

function Nav({ indexStatus, loginStatus, setLoginStatus, setIndexStatus }) {
  const history = useHistory();

  const logoutHandler = () => {
    ...
    setLoginStatus(false); // <-- logged out
  };

  return loginStatus ? (
    ...
  )
  : (
    ...
  );
}

登录页面

通过props访问setLoginStatus状态更新器功能,设置用户登录成功时。使用 Auth HOC 装饰导出(应该为所有需要它的组件完成此操作)。

function LoginPage(props) {
  ...

  const onSubmitHandler=(e)=>{
    e.preventDefault();

    let body={
      email: Email,
      password: Password
    };

    dispatch(loginUser(body))
      .then(response=>{
        if(response.payload.loginSuccess){
          localStorage.setItem('user',response.payload.loginSuccess)
          props.setLoginStatus(true); // <-- set logged in
          props.history.push("/");
        } else {
          alert('login failed');
        }
      });
  }

  return (
    <>
      ...
    </>
  );
}

export default Auth(withRouter(LoginPage));

更新

您的组件没有收到传递的 props 的原因是 Auth HOC 没有传递它们。

export default function(SpecificComponent, option, adminRoute = null) {
  ...
  function AuthenticationCheck(props) { // <-- props
    ...
    return (
      <SpecificComponent /> // <-- not passed through
    );
  }
  return AuthenticationCheck
}

应该是

export default function(SpecificComponent, option, adminRoute = null) {
  ...
  function AuthenticationCheck(props) { // <-- props
    ...
    return (
      <SpecificComponent {...props} /> // <-- passed through
    );
  }
  return AuthenticationCheck
}

【讨论】:

  • 非常感谢您的具体帮助!当我可以通过我的笔记本电脑使用笔记本电脑时,我会尝试它我想要的是通过登录/退出状态在网页上显示不同的导航组件,这就是我使用 ?: operator 的原因。但是您的代码中的 && 运算符不是仅在用户登录时才显示导航组件,而不是在用户注销时显示吗?如果它是正确的,您认为我可以解决它来制作 2 个导航组件(一个只是导航,另一个是用于用户) 并使用 ?: 运算符在 app.js 中渲染它们?如果我的想法有误,请告诉我,并再次感谢您的帮助
  • @user15508209 我查看了您的Nav 组件,它只有一个logoutHandler 处理程序,而您为这两种情况呈现了几乎相同的JSX,所以我认为这是错误的。听起来我假设不正确。我会更新答案。
  • 感谢您的好意。我没有让它太不同,因为我想先完成条件渲染。哈哈,我也有一个小问题。我试过了
  • } />
  • 以上但它一直说 props.setLoginStatus 不是一个功能。我也 console.log(props) 但在任何地方都找不到 setLoginStatus
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-23
  • 2020-02-18
  • 1970-01-01
  • 2019-03-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多