Home > Software design >  Parent state not updated correctly by children
Parent state not updated correctly by children

Time:10-20

I am passing the increment function to a list of children (Row), but the count is never actually changed, I know that something about doing this in the children's useEffect is off. But I am still not able to understand this behavior.

Also, I am not setting the dependency array, because in this case, the count will run infinitely.

import { useCallback, useEffect, useState } from "react";
import "./styles.css";

const list = ["One", "Two", "Three"];

export default function App() {
  const [count, setCount] = useState(0);

  const handleOnClick = useCallback(() => {
    setCount(count   1);
  }, [count]);

  useEffect(() => {
    if (list.length === count) {
      alert("yaaay!");
    }
  }, [count]);

  return (
    <div className="App">
      <h1>Count is: {count}</h1>
      {list.map((item) => (
        <Row key={item} name={item} addOne={handleOnClick} />
      ))}
    </div>
  );
}

const Row = ({ addOne, name }) => {
  useEffect(() => {
    addOne();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return <p>{name}</p>;
};

The output is:

Count is: 1

One

Two

Three

Expected:

Count is: 3

One

Two

Three

CodePudding user response:

okay i've created a demo

Couple of things. first, you need to set the useState values through the callback function since we are updating the counter value continuously

Secondly, need to use useRef in the child component to make sure child component is visited In each iteration. Do the checking inside child component useEffect

export default function App() {
  const [count, setCount] = React.useState(0);
    
  const handleOnClick = React.useCallback(() => {
    setCount((count) => count   1);
  }, [count, setCount]);

  React.useEffect(() => {
    if (list.length === count) {
      alert('yaaay!');
    }
  }, [count]);

  return (
    <div className="App">
      <h1>Count is: {count}</h1>
      {list.map((item) => (
        <Row key={item} name={item} addOne={handleOnClick} />
      ))}
    </div>
  );
}

const Row =({ addOne, name }) => {
  const dataFetchedRef = React.useRef(false);

  React.useEffect(() => {
    if (dataFetchedRef.current) return;
    dataFetchedRef.current = true;
    addOne();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [name]);

  return <p>{name}</p>;
};
  • Related