Error: Too Many re-renders. React Limits the number of renders to Prevent an Infinite loop

Asked

Viewed 2,038 times

0

Eai, I started with reactjs a short time ago and decided to make a site with simple functions to learn, one of these functions would be to list in card format some data I receive from the backend, but when I try to list the cards presents the error:

Error: Too Many re-renders. React Limits the number of renders to Prevent an Infinite loop.

I took a look at the net about this error but did not understand very well what could be causing this error in my application, I have two components one would be the component Pagehome, which brings together all the components of the page, and the component Card, I will leave the code of the two components down, if someone can identify where the error could be and explain why it occurs would help a lot, thank you.

Component code PageInicial:

import React, { useState, useEffect } from "react"
import NavBar from "../../componentes/navbar/navbar"
import { Container, Row, Col } from 'reactstrap'
import api from '../../api'
import CardAnime from '../../componentes/card/card' 

const PageInicial = () => {

    const [animes, setAnimes] = useState([]);
    const [pageTotal, setPageTotal] = useState(0);
    const [pageAtual, setPageAtual] = useState(1);
    const [limitePorPage, setLimitePorPage] = useState(40);

    useEffect(() => {
        api.get(`/animes/listar/${pageAtual}/${limitePorPage}`).then((response) => {
            if (response.data.status) {
                console.log(response.data.erro);

            } else {
                setAnimes(response.data.animes);
                setPageTotal(response.data.pageTotal)
            }
        })
        

    }, [pageAtual]);
    return (
        <div>

            <NavBar page="home" />
            <Container fluid >
                <Row >
                    {
                        animes.map((anime)=>{
                            return (
                                <Col key={anime.id_lista} xl="3" lg="3" md="4" sm="6" >
                                    <CardAnime anime={anime} />
                                 </Col>
                             )
                        })
                    }
               
                </Row>
            </Container>

        </div>
    )
}

export default PageInicial;

Component code Card:

import React, { useState, useEffect } from 'react';
import {
    Modal, ModalHeader, ModalBody, ModalFooter,
    Card, Badge, CardImg, CardText,
    CardBody, CardTitle, CardSubtitle,
    Button, ListGroup, ListGroupItem
} from 'reactstrap';
import './card.css';
import YoutubeEmbed from '../video/video';
import api from "../../api"
import { useHistory, Link } from "react-router-dom";
import semanime from "../../img/semanime.jpeg"



const CardAnime = (props) => {


    const [modal, setModal] = useState(false);
    const [modalLista, setModalLista] = useState(false);
    const [generos, setGeneros] = useState([" "," "," "]);
    const [idanime, setIdanime] = useState("");
    const [lista, setLista] = useState(false)

    const { anime, noanime, openlista, login, className } = props
    const token = localStorage.getItem('TOKEN')
    const userid = localStorage.getItem('ID')
    const history = useHistory();

    const toggle = () => setModal(!modal);
    const modlista = () => setModalLista(!modalLista);

    if(anime){
        setGeneros(anime.genero.split(","));
        if (generos.length > 3) {
            while (generos.length > 3) {
              generos.pop();
            }
          }
    }

    return (
        <div>
            <Card className="space">
                <CardImg top width="100%" className="img" src={anime.imagem} alt="Card image cap" />
                <CardBody>
                    <CardTitle tag="h5" >{anime.nome}</CardTitle>
                    <CardSubtitle tag="h6" className="mb-2 text-muted">
                        {
                            noanime ? "" :
                                generos.map((genero, index) => {
                                    return <Badge key={index} href="#" color="light">{genero}</Badge>
                                })
                        }
                    </CardSubtitle>
                    <CardText>
                        <ListGroup id={noanime ? "off" : "on"}>
                            <ListGroupItem> <strong>Episódios: </strong> {anime.num_ep} </ListGroupItem>
                            <ListGroupItem> <strong>Status: </strong> {anime.status} </ListGroupItem>
                        </ListGroup>
                    </CardText>
                    <Button color="success" id={noanime ? "off" : "on"} size="lg" onClick={openlista ? modlista : toggle} block>Ver</Button>
                </CardBody>
            </Card>

            <Modal isOpen={modal} toggle={toggle} className={className}>
                <ModalHeader toggle={toggle}>{anime.nome}</ModalHeader>
                <ModalBody>
                    <YoutubeEmbed embedId={anime.trailer} />
                    <hr />
                    <ListGroup>
                        <ListGroupItem> <strong>Generos: </strong> {anime.genero} </ListGroupItem>
                        <ListGroupItem> <strong>Episódios: </strong> {anime.num_ep} </ListGroupItem>
                        <ListGroupItem> <strong>Status: </strong> {anime.status} </ListGroupItem>
                    </ListGroup>
                </ModalBody>
                <ModalFooter >
                    <div className="div">
                        <a id={login ? "off" : "on"} href="/login">Faça login para adicionar em listas</a>{' '}
                        <Button color="success" id={login ? "on" : "off"} onClick={() => { setIdanime(anime._id); setLista("assistir") }} >Assistir</Button>
                        <Button color="success" id={login ? "on" : "off"} onClick={() => { setIdanime(anime._id); setLista("assistidos") }} >Assistidos</Button>
                        <Button color="danger" onClick={toggle}>Fechar</Button>
                    </div>
                </ModalFooter>
            </Modal>
            <Modal isOpen={modalLista} toggle={modlista} className={className}>
                <ModalHeader toggle={modlista}>{anime.nome}</ModalHeader>
                <ModalBody>
                    <YoutubeEmbed embedId={anime.trailer} />
                    <hr />
                    <ListGroup>
                        <ListGroupItem> <strong>Generos: </strong> {anime.genero} </ListGroupItem>
                        <ListGroupItem> <strong>Episódios: </strong> {anime.num_ep} </ListGroupItem>
                        <ListGroupItem> <strong>Status: </strong> {anime.status} </ListGroupItem>
                    </ListGroup>
                </ModalBody>
                <ModalFooter >
                    <div className="div">
                        <Button color="success" id="on" onClick={() => { setIdanime(anime._id); setLista("assistidos") }} >Assistidos</Button>
                        <Button color="danger" onClick={() => {
                        }}>Excluir</Button>
                    </div>
                </ModalFooter>
            </Modal>
        </div>
    );
};

export default CardAnime;

1 answer

1

I believe your problem lies in this part of the component CardAnime.

if(anime){
    setGeneros(anime.genero.split(","));
    if (generos.length > 3) {
        while (generos.length > 3) {
          generos.pop();
        }
      }
}

So you invoke the method setGeneros this state change ends up generating the re-rendering of its component, which in turn executes the setGeneros looping again.

I suggest you put that stretch inside a useEffect.

useEffect(() => {
if(anime){
    setGeneros(anime.genero.split(","));
    if (generos.length > 3) {
        while (generos.length > 3) {
          generos.pop();
        }
      }
}
}, [anime])

This should be enough to solve the loop in your code.

Another point of attention in your code is this excerpt:

if (generos.length > 3) {
        while (generos.length > 3) {
          generos.pop();
        }
      }

The function pop removes the last item from its generos, meanwhile the pop is mutating the object directly, it is possible that you have problems in the future by mutating a state in this way.

I believe your goal is to present only the first three genres, so I suggest you first do the necessary verification and if there really is more than three elements we can use the function slice to generate a new array only with the first three values.

useEffect(() => {
if(anime){
    const gen = anime.genero.split(",")
    if(generos.length > 3) setGeneros(gen.slice(0, 3));
    else setGeneros(gen)
}
}, [anime])
  • I made the changes in the code and solved so far no longer presented the error, thanks for the genre tip

  • One correction: dependence on useEffect must be anime because of the if (anime). Including, by if I assume that anime can be undefined or null, in that case use anime.genero in dependence would cause an exception.

  • You are right Rafael, I did not connect in this detail, I edited the reply correcting the dependency useEffect, thank you very much.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.