Home > Blockchain >  OnChange Event Stuck in Loop, Crashing
OnChange Event Stuck in Loop, Crashing

Time:06-07

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)}
  • Related