Make conditional react component rerender after boolean changes

4.2k views Asked by At

I have a react component that conditionally renders JSX according to the user's login state.

<div>
    { boolIsLoggedIn ?
    <SomeLoggedInComponent /> : <SomeNotLoggedInComponent /> }
</div>

I think I need to use React.useState() and/or React.useEffect() but I'm not sure exactly how to implement it.

I've tried this:

const [boolIsLoggedIn, setBoolIsLoggedIn] = useState(isLoggedIn())

useEffect(() => {
  const checkLogIn = () => {
    setBoolIsLoggedIn(isLoggedIn())
  }

  checkLogIn()
})

Where isLoggedIn() checks whether the user is logged in or not and returns a boolean.

Currently, I can log out and log in and the isLoggedIn() function works, but the component I want to conditionally re-render doesn't do so until I refresh the page.


So I added [isLoggedin()] as the second parameter to useEffect() and now it almost works. When I log in, the boolIsLoggedIn value changes to true and the component re-renders. However, when I log back out boolIsLoggedIn doesn't change until I refresh the page. Here is my code for the isLoggedIn() function, which is coded in a seperate file and imported:

let boolIsLoggedIn = false

export const setUser = user => {
  //handle login
  boolIsLoggedIn = true

export const logout => {
  //handle logout
  boolIsLoggedIn = false
}

export const isLoggedIn = () =>
    return boolIsLoggedIn
}
4

There are 4 answers

0
Mohammad Ali Rony On

try this

import React, { Component } from "react";
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoggedIn: isLoggedIn()
    };
  }

  render() {
    let { isLoggedIn } = this.state;

    return (
      <div className="App">
       
        {(function() {
          if (isLoggedIn) {
            return <div>Login</div>;
          } else {
            return <div>with out Login</div>;
          }
        })()}
      </div>
    );
  }
}

export default App;

4
Șold Andrei-Alexandru On
  1. You are missing the open [ on defining the state.
  2. I would set the defailt value on false so you won't execute the function twice.
  3. The use effect usually needs a dependencies array as the second parameter but as you only want to run it once (after the component is mounted) just place an empty array as the second parameter.
  4. Another thing is that if the isLoggedIn function is asyncronous you have to wait for it by using await and setting the parent function as async. This would definetly be the problem you have if the function is asyncronous.
0
crispengari On

Change This:

const boolIsLoggedIn, setBoolIsLoggedIn] = useState(isLoggedIn())

useEffect(() => {
  const checkLogIn = () => {
    setBoolIsLoggedIn(isLoggedIn())
  }

  checkLogIn()
})

To this:

const [boolIsLoggedIn, setBoolIsLoggedIn] = useState(isLoggedIn());

  useEffect(() => {
    (() => {
      setBoolIsLoggedIn(isLoggedIn());
    })();
  }, [isLoggedIn()]);

Good Luck

0
geofrey On

You need to be able to share react state, and not just values in order to trigger a react re-render. In your example, you would need a call to setBoolIsLoggedIn() whenever value of isLoggedIn() changes in order to change the state and trigger a re-render of the component.

Another way share state between components is through React Context. You would first need to create a Context Provider like this:

const UserContext = React.createContext();

export function UserProvider({ children }) {
    const [ user, setUser ] = useState({ loggedIn: false });

    return (
        <UserContext.Provider value={[ user, setUser ]}>
            { children }
        </UserContext.Provider>
    )
}

In this case, the UserProvider is the one maintaining the shared state withuser={ loggedIn }. You would then need to wrap your React App component with this provider in order for components to be able to share state.

To set the shared user state you can use the hook useContext(UserContext) and change state through setUser provided by the hook. In the example below, we have a login button component that sets shared user state:

function LoginButton() {
  const [user, setUser] = useContext(UserContext);

  /** flip login state when button is clicked */
  function logIn() {
    setUser(state => ({ ...state, loggedIn: !state.loggedIn }) );
  }

  return (
    <button onClick={logIn}>{ user.loggedIn ? "Log Out" : "Log In"}</button>
  );
}

And finally, to be able to use the shared user state and trigger a re-render whenever the user state changes, you would again use the hook useContext(UserContext) in a different component to be able to get the state like this:

export default function App() {
  const [user,] = useContext(UserContext);

  return (
    <div className="App">
      <h1>Conditional React component</h1>
      <h2>Click on the button to login/logout</h2>
      <LoginButton></LoginButton>

      <div>
        { user.loggedIn ? <LoggedInComponent/> : <LoggedOutComponent/>}
      </div>
    </div>
  );
}

I've provided an example here to show how this all works together.