1

So I have a very complex structure but I think I'm very clear on the issue and thus I'm mentioning the minimum requirement for the issue.

Consider following is a component that I've used.

const SalesOrderLineItems = ({
  ...requiredProps,
}) => {
  const [productLines, setProductLines] = useState([])
  const [shippingLines, setShippingLines] = useState([])
  const [totals, setTotals] = useState({})
  const [discounts, setDiscounts] = useState([])

  useEffect(() => {
    // My main concern is this. I'd expect this to be triggered whenever I use setState in child component.
    console.log(' ~ productLines:', productLines)
  }, [productLines])

  const updateProductLines = useCallback((lines) => setProductLines(lines), [])
  const updateShippingLines = useCallback((lines) => setShippingLines(lines), [])
  return (
    <div className="SalesOrderLineItems">
      <SalesOrderProductLines
        updateProductLines={updateProductLines} 
        {...otherProps} />
      <SalesOrderShippingLines
        updateShippingLines={updateShippingLines}
        productLines={productLines} // Mainly for calculative purposes, Never used in rendering
        {...otherProps}
      />
      <SalesOrderDiscounts
        discounts={discounts}
        {...otherProps}
      />
      <SalesOrderTotals
        totals={totals}
        {...otherProps}
      />
    </div>
  )
}
export default SalesOrderLineItems

Now in SalesOrderProductLines component, I've used React Hook Form's watch which updates the productLines via passed prop like below.

useEffect(() => {
    const subscription = watch((value, { name, type }) => {
      // some business logic
      updateProductLines(value.lines)
      console.log(' ~ lines updated:', value.lines) // This gets logged with the latest values, So I'm assured that the above state update works fine!
    })
    return () => subscription.unsubscribe()
  }, [watch])

I think that my code uses uncontrolled inputs so it doesn't trigger any re-rendering thus preventing parent component to trigger the effect for productLines!

Do someone know how I can utilize something to find a way to listen to productLines change without triggering re-rendering for the performance changes? As I'm just going to set totals by performing calculations on the latest productLines, shippingLines and discounts.

Codesandbox can be found here

Parent state doesn't trigger effect as per seen here

Mr.Online
  • 306
  • 4
  • 15

1 Answers1

1

Following this answer looks like the problem is in useState. I totally recommend you to check that answer, but anyways, that work:

// SalesOrderProductLines.jsx
  useEffect(() => {
    const subscription = watch((value, { name, type }) => {
      // ...some business logic
      console.log("Parent component will not log this changes", value.lines);
      // you have to imitate previous state mutation or smth like this:)
      updateProductLines((p) => [...value.lines]);
    });
    return () => subscription.unsubscribe();
  }, [watch, updateProductLines]);

Ipa Stor
  • 158
  • 1
  • 5
  • 1
    and btw, I'm not sure you need `const updateProductLines = useCallback((lines) => setProductLines(lines), [])`, as for me, you can send in props `setProductLines` because `watch(...)` is an useCallback with observer so I don't think you'll have an infinite rerender or anything else. I put `setProductLines` strict to props and it worked fine – Ipa Stor May 08 '23 at 06:20
  • Thanks for the answer @Ipa Stor. It's working as expected now. Will reward your bounty in 21 hour! – Mr.Online May 08 '23 at 06:24