Search code examples
javascriptreactjsrendering

How to rerender functional component on state change?


I want my functional component to rerender when the state "dataRes" changes. The code below is the whole component but on first login I have to reload manually to display the avatar/username etc.

As soon as "loggedin" changes to "true" it should rerender and display username. Well the displaying works perfectly fine just the first rerender doesn't...

Component:

function Username() {
  const cookies = new Cookies();

  const [username, setUsername] = useState(false);
  const [loading, setLoading] = useState(true);
  const [shortened, setShortened] = useState(false)
  const authLink = "https://discord.com/api/oauth2/authorize?client_id=" + clientID + "&redirect_uri=" + clientRedirect + "&response_type=code&scope=identify%20guilds"
  const [loggedin, setLoggedin] = useState(false);
  const history = useHistory();
  const [data, setData] = useState(null);
  const [dataRes, setDataRes] = useState(false);

  useEffect(() => {
    fetch('SomeAPIUrlHere', {
      method: "POST",
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ token: cookies.get("someCookie") })
    })
    .then((res) => res.json())
    .then((data) => {
      setData(data)
      setDataRes(true)
    })
  }, []);

  try {
    if(dataRes !== false) {
      if(data.username) {
        if(username != data.username){
          if(data.username.length > 10) {
            setShortened(true)
          }
          setDataRes(false)
          setUsername(true)
          setLoggedin(true)
          setLoading(false)
        }
      }
    }
  } catch(error) {
    console.log(error)
  }

  useEffect(() => {
    setTimeout(() => {
      setLoading(false)
    }, 1000)
  }, [])

  const SignOut = () => {
    const cookies = new Cookies();
    cookies.remove("us_au_tok")
    history.push("/")
    window.location.reload(false)
  }

  return(
    <div className="flex float-left">
      <a href={loggedin === false ? authLink : "/dashboard"} className={loggedin === false ? "shadow-2xl text-center  text-white bg-gray rounded-2xl p-2 lg:px-5 md:px-5 sm:pl-3.5 xs:pl-3.5 mr-2 block lg:w-40 md:w-40" : "text-center text-white  rounded-2xl p-2 lg:px-5 md:px-5 sm:pl-3 xs:pl-3 mr-2 block lg:w-48 md:w-48"}>
        <div className={loading === true ? "md:w-8 lg:w-8 sm:w-7 xs:w-7 m-auto text-center" : "m-auto text-center"}>
          <div className="flex justify-nter">
            {loggedin === true ? <img src={"https://cdn.discordapp.com/avatars/" + data.userid  + "/" + data.avatar + ".png?size=1024"} alt="Useravatar" className="lg:max-h-50 md:max-h-50 sm:max-h-35 xs:max-h-35 rounded-full mr-2"></img> : ""}
            {loading === true ? <img src={LoadingSVG} alt='Loading Animation'/> : <p className="hover:text-brightgray duration-300 lg:m-auto md:m-auto sm:my-auto xs:my-auto">{loggedin === true ? <span>{shortened === false ? data.username : data.username.substring(0, 10) + "..."}</span> : "Login"}</p> }
          </div>
        </div>
      </a>
      {loggedin === true ? <button onClick={SignOut}><FontAwesomeIcon icon={faSignOutAlt} className="text-white mr-4" title="Sign Out" /></button> : ""}
    </div>
  )
};

Solution

  • You should pass dataRes as second argument to useEffect

    The second argument to useEffect is an array of dependencies . If you want to control when effect is to be executed after mounting the component, then pass an array of dependencies as the second argument

    here is a sample:

     const {useEffect,useState} = React;
    
    function App () {
      const [data, setData] = useState(null);
      const [dataRes, setDataRes] = useState(false);
      useEffect(() => {
        if (dataRes) {
          fetch("https://gorest.co.in/public/v1/users", {
            method: "GET"
          })
            .then((res) => res.json())
            .then((data) => {
              setData(data);
              //setDataRes(true);
            });
        } else {
          setData([]);
        }
      }, [dataRes]);
      const handleClick = () => {
        setDataRes((prev) => !prev);
      };
      return (
        <div className="App">
           <button onClick={handleClick}>change dataRes</button>
          <div>{`dataRes: ${dataRes}`}</div>
          <hr />
          <div>{JSON.stringify(data)}</div>
        </div>
      );
    }
    
    ReactDOM.render( <App/> ,
      document.getElementById("root")
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="root"></div>