首页 > 解决方案 > 为什么即使我使用了 useEffect 我的组件也不会重新渲染自己?(React.js)

问题描述

最近,我正在制作我的 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]

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

标签: javascriptreactjs

解决方案


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

应用程序

loginStatus状态移动到这个父组件。将 传递loginStatusNav组件以使其处理其条件渲染,并将 传递setLoginStatusLogin组件以使其更新状态。请注意,路由已重新排序,因此它们不再需要在exact任何地方指定道具,它们没有被AuthHOC 包装,因为这可能会在每个渲染周期创建一个新组件,现在该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 中访问setLoginStatusstate updater 功能,并设置用户登录成功的时间。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));

更新

您的组件没有收到传递的道具的原因是因为AuthHOC 没有传递它们。

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
}

推荐阅读