Why does this create an infinite render loop with React hooks?

Viewed 1269

I am trying to render beer names as H1 elements in a alphabetical sorted order and I'm new to React Hooks but this seems to cause a re-render loop

The Beer component is passed an array of beers as props from the Home component which utilises the useEffect Hook which I suspect may be the culprit although I'm not sure.

const Beer = ({ beer }) => {
  const [beers, setBeers] = useState([]);
  let rendered;
  if (beer === undefined) {
    rendered = <Spinner />;
  } else {
    rendered = beer.map(beer => beer.name);
    setBeers(rendered.sort());
    rendered = beers.map(beer => <h1>{beer}</h1>);
  }

  return <div>{rendered}</div>;
};

const Home = () => {
  const [beers, setBeers] = useState();
  useEffect(() => {
    const getBeers = async () => {
      const beerData = await fetch('https://api.punkapi.com/v2/beers');
      const beers = await beerData.json();
      console.log(beers);
      setBeers(beers);
    };
    getBeers();
  }, []);

  return (
    <div>
      <h1>Punk Beers</h1>
      <Beer beer={beers} />
    </div>
  );
};

I expect there to be a rendered list of H1 elements but this produces an endless loop of renders.

1 Answers

When you call setBeers inside of Beer React will re-render the component (i.e. re-execute the function) with the updated value being provided for the beers state variable.

You generally do not need to copy props into state like this. If you have a component that performs an expensive operation on props before displaying, you may want to use memoization to cache the result.

In this case, simply sorting beers isn't expensive unless your array is thousands and thousands of items long, so no optimization is necessary.

const Beer = ({ beers }) => (
  beers ? (
    [...beers].sort().map((beer, index) => 
      <h1 key={index}>{beer}</h1>
    )
  ) : (
    <Spinner />
  )
)

const Home = () => {
  const [beers, setBeers] = useState();
  useEffect(() => {
    const getBeers = async () => {
      const beerData = await fetch('https://api.punkapi.com/v2/beers');
      const beers = await beerData.json();
      console.log(beers);
      setBeers(beers);
    };
    getBeers();
  }, []);

  return (
    <div>
      <h1>Punk Beers</h1>
      <Beer beers={beers} />
    </div>
  );
};
Related