-1

i'm wondering how is the best way to get around the Child component being called every time the useState variable is updated. The way I have things currently setup is as follows (stripped down version) -

Parent:

import Child from "../components/child";
import { useState } from "react";

const Home = () => {

return (
        <div>
            <Child />
        </div>
    );
};
export default Home;

Child component:

import { useState } from "react";

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

  async function fetchData(id) {
    while (id < 100) {
      console.log(id)
      setCount(id)
      }
      id = id + 1;
    }
  }

  fetchData(10)

  return (
    <>
        {count}
    </>
  )
}

The parent component gets updated but then calls Child again because of the render and starts it again. how do you stop this kind of behaviour? My "tally" variable is being updated in console no problem but trying to set count to that value rerenders everything causing it to start a second instance of the child component loop

Youssouf Oumar
  • 29,373
  • 11
  • 46
  • 65
user100911
  • 15
  • 5
  • This is how react works. Chaging the state trigger a render. If you don't want a render, you should look for `useRef` – dbuchet Apr 24 '23 at 08:19
  • 1
    You can also useMemo the child component and if the state doesn't affect the child props it won't re-render – leo Apr 24 '23 at 08:27
  • Of course I want a render, I just down want the ENTIRE component to be called over and over again, just want it called once and show the updated count as it happens on the parent, not rerender so it starts from the beginning again so I have 2 lots of counting going on, totally separate of each other, then 3 lots etc etc. I want to see the updated count as it happens, live so useMemo seems to be out since it will only update the parent once counting has finished. – user100911 Apr 24 '23 at 09:07
  • Are you passing any prop from the parent to the child @user100911? – Youssouf Oumar Apr 24 '23 at 09:24
  • You can see in the code there are no props used. Should I be using props? I explained in an answer given below that I moved all the code into the parent and now it works fine. It's something about setting the useState in the child component that sends it haywire, whereas in the parent doing the same thing it works perfectly. – user100911 Apr 24 '23 at 11:14

1 Answers1

2

React is functional. I.e. it does not have component state, i.e., when a render is triggered everything is re-rendered. Typically this is fine as renders are very fast. If you want to store data between renders then you need to use either useState or useMemo. Calling a method fetchData(10) inside the body of function means it's always going to run over and over. It's not clear what exact functionality your expecting but right now this is working as designed. I'd guess fetchData should actually be triggered by some kind of event. Not in the body

There were quite a few errors in your code, which I'm guessing is why you were experiencing problems. The below now works (see here)

Some of the issues:

  • mixed use of arrow and none arrow functions (tl;dr always use arrow functions)
  • the increment in the while loop needs to be inside the loop
  • fetchData should be in some kind of async method (I'm using useEffect but you could also use useMemo, etc)
  • As below there is nothing async in your code, so I've removed the unnecessary async in the function.

import React, { useEffect, useState } from "react";

const Home = () => {
  return (
    <div>
      <Child />
    </div>
  );
};

const Child = () => {
  const [count, setCount] = useState(0);

  const fetchData = (id) => {
    while (id < 100) {
      console.log(id);
      setCount(id);

      id = id + 1;
    }
  };

  useEffect(() => fetchData(10), []);

  return <p>count: {count}</p>;
};
const App = () => {
  return (
    <div className="App">
      <Home />
    </div>
  );
};

export default App;

Of course none of this is async, so that method is entirely pointless. you may as well just set count to 100. As fetchData will simply run on render. Adding the keyword async to a none async function does not make it async.

I'd suggest you read:

Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference

Liam
  • 27,717
  • 28
  • 128
  • 190
  • The thing is if I move the code from the child into the parent it all works fine, no rerender, it shows the count incrementing perfectly. When I try to use a child component and also "setState(count)" in that component, the child rerenders. So instead of seeing 10, 11, 12 and then 13 for example, I'll see 10, 11, 12, 10, 13, 11, 10, 12, 14 etc. What's bewildering is if i'm doing things wrong then why is it seamless when code is in the parent? It doesn't try to start from 10 again when it updates the count on the page. In child it does. It only happens when using the setCount useState though – user100911 Apr 24 '23 at 10:54
  • Yes changing state triggers a re-render and not using state does not. The parent child should be irrlevent – Liam Apr 24 '23 at 10:57
  • it should be irrelevant, but seems it's not. I used the same code in the parent, including the setCount usestate variable and it works perfectly like I said, counter goes up on the page with only the one loop running and stops when it's done, not spawn off countless other counters and never stop like it does when I run it in a child component. There is something wrong with the way I'm using useState in the child. – user100911 Apr 24 '23 at 11:12
  • Right, the problem of the ever increasing value was nothing to do with useState, you'd put the value increment in the wrong place. So the while loop would never stop. See the above working example. https://codesandbox.io/s/react-fiddle-forked-wd3nqf?file=/src/App.js:0-509 – Liam Apr 24 '23 at 13:24
  • Thankyou for the explanation that got it working – user100911 Jun 06 '23 at 11:28