function running before useEffect finishes

I have this code:

const ChatsPage = () => {
    let username = ""
    let secret = ""
    useEffect(() => {
        axios
            .post('http://localhost:3001/authenticate')
            .then((response) => {
                username = response.data.username
                secret = response.data.secret
            })
            .catch((error) => {
                console.log(error);
            });
    }, []);

    let chatProps = useMultiChatLogic('xxxxx-xxx-xxx-xxx-xxx', username, secret);

    return (
        <div style={{ height: '100vh' }}>
            <MultiChatSocket {...chatProps} />
            <MultiChatWindow {...chatProps} style={{ height: '100vh' }} />
        </div>
    );
}

The problem with it is that let chatProps = useMultiChatLogic('xxxx-xx-x-xx-xxx', username, secret); is running before the useEffect is finished. I have tried to move it inside the .then, where it gives a hooks error, and a few other things but nothing has worked.

>Solution :

I think you’re missing a basic understanding of React. Check out a tutorial on state, useEffect and control flow in general.

useEffect is asynchronous–the callback is executed by React after the first render and whenever the variables captured in the dependency array are set. Your dependency array is empty, so this useEffect runs once in the component’s lifecycle, after the first render.

I have no idea what the Multi stuff is, but you could try something like this:

const ChatsPageLoader = () => {
  const [username, setUsername] = useState('');
  const [secret, setSecret] = useState('');

  useEffect(() => {
    axios
      .post('http://localhost:3001/authenticate')
      .then((response) => {
        setUsername(response.data.username);
        setSecret(response.data.secret);
      })
      .catch((error) => {
        console.log(error);
      });
  }, []);

  if (!username || !secret) {
    return <div>Loading...</div>;
  }

  return <ChatsPage username={username} secret={secret} />;
};

const ChatsPage = ({username, secret}) => {
  const chatProps = useMultiChatLogic('xxxxx-xxx-xxx-xxx-xxx', username, secret);

  return (
    <div style={{ height: '100vh' }}>
      <MultiChatSocket {...chatProps} />
      <MultiChatWindow {...chatProps} style={{ height: '100vh' }} />
    </div>
  );
};

Here, on the first render we know the request hasn’t finished because username and secret are still the default empty strings, so we render a loading message. After that’s rendered, the useEffect runs and kicks off the request.

After a few moments, the response arrives and we set state for username and secret, which triggers another render. This time, the username and secret values from the response are available (I assume they’re guaranteed to be nonempty strings), so the loading message isn’t rendered. Instead, we render the ChatsPage, which accepts props with the response’s data.

The extra component is necessary because hooks like useMultiChatLogic have to be declared above any conditions.


The golden rule of React is that state is immutable, so if any data changes from one render to the next, it has to be done through setState, not =.

Leave a Reply