Home > Back-end >  Why I'm getting Rendered more hooks error?
Why I'm getting Rendered more hooks error?

Time:12-06

I was wonder what I'm doing wrong here.

I'm getting this error: "Rendered more hooks than during the previous render."

export default function ProductDetails() {
    //Use State
    const {qty, increaseQty, decreaseQty, onAdd, setQty} = useStateContext();

    //Reset Qty

    useEffect(() => {
        setQty(1);
    }, []);
    //Fetch Slug
    const {query} = useRouter();
    //Fetch Graphql data
    const [results] = useQuery({
        query: GET_PRODUCT_QUERY,
        variables: {slug: query.slug}
    })
    const {data, fetching, error} = results;
     //Check for data coming in
    if(fetching) return <p>Loading...</p>;
    if(error) return <p>Oh no....</p>;
    //Extract Product Data
    const {title,description, image, gallery } = data.products.data[0].attributes;
    const [img, setImg] = useState(gallery.data[0].attributes.formats.medium.url);
    console.log(img);


    //Create a toast 

    const notify = () => {
        toast.success(`${title} added to your cart`, {duration: 1500});
    }

    return(
        <DetailsStyle>
            <Gallery>
                <img src={gallery.data[0].attributes.formats.medium.url} alt={title} />
                <Thumbnails>
                    {gallery.data.map((image, index) => (
                            <SingleThumb key={index} >
                                <img src={image.attributes.formats.thumbnail.url} alt={title} />
                            </SingleThumb>
                        )
                    )}
                </Thumbnails>
            </Gallery>
            <ProductInfo>
                <h3>{title}</h3>
                <p>{description}</p>
                <Quantity>
                    <span>Quantity</span>
                    <button><AiFillMinusCircle onClick={decreaseQty} /></button>
                    <p>{qty}</p>
                    <button><AiFillPlusCircle onClick={increaseQty}/></button>
                </Quantity>
                <Buy onClick={() => {
                    onAdd(data.products.data[0].attributes, qty)
                    notify();
                }}>Add To Cart</Buy>
            </ProductInfo>
        </DetailsStyle>
    )
}

Something wrong is in this line: const [img, setImg] = useState();

Why I can't use more hooks here.

Does anyone know why I'm getting this?

CodePudding user response:

You are using early return

and this line of code won't execute every time:

    const [img, setImg] = useState(gallery.data[0].attributes.formats.medium.url);

CodePudding user response:

This is only conditionally called:

const [img, setImg] = useState(gallery.data[0].attributes.formats.medium.url);

Because the component has earlier conditional return statements. Move it to earlier in the function. (Generally I invoke useState operations right away.)

Hooks need to always be consistently called in the same order on every render.

CodePudding user response:

You declare your state after some return statements. It means that if you had any errors or you were in loading state, the state is not defined. But maybe in the next render, the data is set and then your state will be defined with the inital value (gallery.data[0].attributes.formats.medium.url).

It's forbidden in react because all of the hooks should always be in the same order on every single render. In order to fix this, you should change the place of your useState for img.

Hope it helps:

export default function ProductDetails() {
    const [img, setImg] = useState('');
    
    //Use State
    const {qty, increaseQty, decreaseQty, onAdd, setQty} = useStateContext();

    //Reset Qty

    useEffect(() => {
        setQty(1);
    }, []);
    //Fetch Slug
    const {query} = useRouter();
    //Fetch Graphql data
    const [results] = useQuery({
        query: GET_PRODUCT_QUERY,
        variables: {slug: query.slug}
    })
    const {data, fetching, error} = results;
    //Check for data coming in
    //Extract Product Data

    useEffect(() => {
        if(results && results.data) {
          const {data} = results
          const { gallery } = data.products.data[0].attributes;
          setImg(gallery.data[0].attributes.formats.medium.url);
        }
    }, [results]);

    useEffect(() => {
        console.log(img);
    }, [img]);

    //Create a toast 
    const notify = (title) => {
        toast.success(`${title} added to your cart`, {duration: 1500});
    }

    if(fetching) {
        return <p>Loading...</p>;
    } else if(error) {
        return <p>Oh no....</p>;
    } else if(data) {
        const { title, description, image, gallery } = data.products.data[0].attributes;

        return(
            <DetailsStyle>
                <Gallery>
                    <img src={gallery.data[0].attributes.formats.medium.url} alt={title} />
                    <Thumbnails>
                        {gallery.data.map((image, index) => (
                                <SingleThumb key={index} >
                                    <img src={image.attributes.formats.thumbnail.url} alt={title} />
                                </SingleThumb>
                            )
                        )}
                    </Thumbnails>
                </Gallery>
                <ProductInfo>
                    <h3>{title}</h3>
                    <p>{description}</p>
                    <Quantity>
                        <span>Quantity</span>
                        <button><AiFillMinusCircle onClick={decreaseQty} /></button>
                        <p>{qty}</p>
                        <button><AiFillPlusCircle onClick={increaseQty}/></button>
                    </Quantity>
                    <Buy onClick={() => {
                        onAdd(data.products.data[0].attributes, qty)
                        notify(title);
                    }}>Add To Cart</Buy>
                </ProductInfo>
            </DetailsStyle>
        )
    } else {
      return null;
    }

}

CodePudding user response:

There should be no return before hooks.

These lines

if(fetching) return <p>Loading...</p>;
if(error) return <p>Oh no....</p>;

should be after all hooks

  • Related