I am getting an infinite loop / crash here.
I'm trying to get an onChange event fired for these radio buttons (which are built after pulling data from a query), but I think it keeps redrawing and I can't figure out why.
Any thoughts on how I can solve this?
const GetChallenge = async () => {
const slug = useParams()
const data = await shopifyApolloClient.query({ query: singleProduct(slug) })
return data
}
const Challenge = () => {
let [loaded, setLoaded] = useState(false)
let [product, setProduct] = useState([])
let [variants, setVariants] = useState([])
let [options, setOption] = useState()
let [metafields, setMetafields] = useState([])
GetChallenge().then((ret) => {
setProduct(ret.data.product)
setVariants(ret.data.product.variants.edges)
setOption(ret.data.product.variants.edges[0].node.title)
setMetafields(ret.data.product.metafields.edges)
setLoaded(true)
})
const handleOptions = (event) => {
setOption(event.target.value)
}
if (loaded === true) {
return (
<div>
{variants.map((e) => (
<label
htmlFor={e.node.title}
key={e.node.id}>
<input
type="radio"
name="options"
checked={e.node.title === options}
value={e.node.title}
onChange={handleOptions}
/>
{e.node.title}
</label>
))}
</div>
)
} else {
return (
<p>Not Loaded</p>
)
}
}
CodePudding user response:
GetChallenge
is triggering every render. Try useEffect with the empty array empty soas to trigger only onmount.
import React, { useState, useEffect } from 'react';
const GetChallenge = async () => {
...
useEffect(() => {
GetChallenge().then((ret) => {
setProduct(ret.data.product)
setVariants(ret.data.product.variants.edges)
setOption(ret.data.product.variants.edges[0].node.title)
setMetafields(ret.data.product.metafields.edges)
setLoaded(true)
}),[]}
...
}
CodePudding user response:
The problem is not the onChange, but the GetChallenge being called in the function's body. You should call GetChallenge in a useEffect,passing an empty dependencies array. As of now you are calling GetChallenge, which is updating your state, which is causing the component to re-render and hence re-call GetChallenge, going in an infinite loop. What you want to do is calling it just once, at mount, and you achieve this with useEffect.
useEffect(() => {
GetChallenge().then() // rest of the function
}, []}
CodePudding user response:
hope this will solve your problem :)
import React, { useState, useEffect } from 'react';
const GetChallenge = async () => {
const slug = useParams();
const data = await shopifyApolloClient.query({ query: singleProduct(slug) });
return data;
};
const Challenge = () => {
const [data, setData] = useState({
loaded: false,
product: [],
variants: [],
options: "",
metafields: []
});
const { loaded, variants, options } = data;
useEffect(() => {
GetChallenge().then((ret) => {
setData((prevState) => ({
...prevState,
product: ret.data.product,
variants: ret.data.product.variants.edges,
options: ret.data.product.variants.edges[0].node.title,
metafields: ret.data.product.metafields.edges,
loaded: true
}));
});
}, []);
const handleOptions = (event) => {
setData((prevState) => ({ ...prevState, options: event.target.value }));
};
if (loaded === true) {
return (
<div>
{variants.map((e) => (
<label htmlFor={e.node.title} key={e.node.id}>
<input
type="radio"
name="options"
checked={e.node.title === options}
value={e.node.title}
onChange={(event) => handleOptions(event)}
/>
{e.node.title}
</label>
))}
</div>
);
} else {
return <p>Not Loaded</p>;
}
};
CodePudding user response:
Try this:
onChange={(event) => setOption(event.target.value)}