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