简体   繁体   中英

Does a react component using useState hook rerender every `setState` call?

https://codesandbox.io/s/mow5zl5729

import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import axios from "axios";

function useLoading() {
  const [isLoading, setLoading] = React.useState(true);
  const [hasError, setError] = React.useState(false);

  const loadStuff = aPromise => {
    return aPromise
      .then(() => {
        setLoading(false);
      })
      .catch(() => {
        setLoading(false);
        setError(true);
      });
  };

  return { isLoading, hasError, loadStuff };
}

function App() {
  const { isLoading, hasError, loadStuff } = useLoading();

  useEffect(() => {
    loadStuff(axios.get(`https://google.com`));
  }, []);

  console.log(isLoading, hasError);

  return <div />;
}

This is a simplified example of what I mean.

If the promise inside useLoading is rejected, I would have expected the component to render on mount and then render the second time when the error is caught. So, I expect a total of 2 renders with the following state:

1st render:

  • isLoading: true
  • hasError: false

2nd render:

  • isLoading: false
  • hasError: true

Instead, it seems that the component rerenders once after setLoading(false) and again after setError(true) . So, I get this:

1st render:

  • isLoading: true
  • hasError: false

2nd render: ( why? )

  • isLoading: false
  • hasError: false

3rd render:

  • isLoading: false
  • hasError: true

I suspect that the issue is somehow my use of the promise inside useEffect but I'm not sure where my mental model went wrong.

EDIT:

When I change useLoading to only contain 1 useState , the problem goes away.

broken:

const [isLoading, setLoading] = React.useState(true);
const [hasError, setError] = React.useState(false);

works:

const [loadingState, setLoadingState] = React.useState({
  loading: true,
  error: false
});

Looks like this has something to do with batching of the state updates. As far as I know React based events will trigger the batch updates but not something that gets triggered outside it. promise in this case.

Because the state calls are not batched, you see the 2nd render where both are set to false

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM