0

I have an object state priceRanges in component A , which i initialize with a key and a value:

const [priceRanges, setPriceRanges] = useState({
        [randomId]: {
            "price": 0,
            "quantity": 0
        }
    })

And then pass it through props to component B:

<ComponentB pricesRanges={pricesRanges} ...props />

The problem is that in Component B , I am converting this object into an array on useEffect:

React.useEffect(() => {
        const getArrayData = () => {
            const keys = Object.keys(priceRanges)
            keys.map(key => {
                console.log(priceRanges[key]["price"])
                priceRangesArray.push({
                    id: key,
                    price: priceRanges[key]["price"] ? priceRanges[key]["price"] : 0,
                    quantity: priceRanges[key]["quantity"] ? priceRanges[key]["quantity"] : 0
                })
            })
        }
        getArrayData()
    }, [priceRanges])

But the component does not rerender after the useEffect has run. As such, the component does not render the initial state that pricesRanges was initialized with. To test this, I did this:

{priceRangesArray.length > 0 ? 
            (priceRangesArray.map(quantityAndPrice => {
                return (
                    <div className="flex" key={quantityAndPrice.id}>
                        {console.log("I AM RENDERING")}
                        <input
                            ...props
                        />
                        <input
                            ...props
                        />
                    </div>
                )
            })) :<p>Hello</p>}

Which always renders Hello on mount . How can I tackle this ? Thanks you !

4
  • why do you have a computed property when you "initialize with a key and value"? Commented Jan 17, 2022 at 15:08
  • @SangeetAgarwal are you refering to the randomId? If yes, it is a uuid v4() .. Commented Jan 17, 2022 at 15:10
  • 1
    Bad idea to mutate a state. Use another a state in ComponentB which will be calculated from priceRanges in your useEffect Commented Jan 17, 2022 at 15:23
  • @dbuchet True dat. Commented Jan 17, 2022 at 15:24

1 Answer 1

2

The only way to cause a rerender is to set state. Mutating an array during a useEffect will not cause a rerender. So you could fix this by adding a state variable, and then setting state once you've calculated the array.

However, i think that's not the right approach here. Your array is derived data: it's directly based on what's in priceRanges, and you don't want it to ever deviate from that. So for this, i would calculate the value in line during rendering the component:

const Example = ({ priceRanges }) => {
  const keys = Object.keys(priceRanges);
  const priceRangesArray = keys.map((key) => {
    return {
      id: key,
      price: priceRanges[key]["price"] ? priceRanges[key]["price"] : 0,
      quantity: priceRanges[key]["quantity"] ? priceRanges[key]["quantity"] : 0,
    };
  });

  // ...rest of the component
}

If performance is a concern, you can wrap this in useMemo to skip the calculation if priceRanges has not changed.

const Example = ({ priceRanges }) => {
  const priceRangesArray = useMemo(() => {
    const keys = Object.keys(priceRanges);
    return keys.map((key) => {
      return {
        id: key,
        price: priceRanges[key]["price"] ? priceRanges[key]["price"] : 0,
        quantity: priceRanges[key]["quantity"] ? priceRanges[key]["quantity"] : 0,
      };
    });
  }, [priceRanges]);

  // ... rest of the component
};
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.