pomber/didact

useState + setTimeout = broken state setter

shadowvzs opened this issue · 3 comments

const SubTest = () => {
    const [s, setS] = useState(10);
    setTimeout(() => {
        const newValue = Math.random();
        console.log('----', newValue)
        setS(s => newValue);
    }, 1000);
    return (
        <h3> Count: {s} </h3>
    );
}

const Test = (): JSX.Element => {
    const [s, setS] = useState(10);

    setTimeout(() => {
        const newValue = Math.random();
        console.log(newValue)
        setS(s => newValue);
    }, 2000);

    return (
        <div>
            <h1>
                Count: {s} {s1}
            </h1>
            <SubTest />
            <SubTest />
        </div>
    );
}

// ---------------------

const MainCmp = (props: any) => {
    return (
        <div>{Test()}</div>
    );
}

const element = <MainCmp />;
const container = document.getElementById("root")
render(element, container!)

and the result is:
image

  • Main (broken): the setter in Test component not change the state value (s, it is always 10), refresh the component but the value remain the initial.

  • Secondary (sometimes broken after 1st change): counter in SubTest sometimes work, sometimes break after first value change.

no console error, the newValue is correct, component is rerun each time just the state saving issue

somehow always a new hook created which not conneted with the dom and never deleted the old one so at end browser with freeze after 15-30 sec

Hey. It isn't a good idea to call setTimeout inside the render function.
Remember that render is called many times, and for each render you are creating 3 timeouts, and each timeout creates 3 more every time it's fired, so you end up with timeouts growing exponentially, and that will freeze your browser.

Hey. It isn't a good idea to call setTimeout inside the render function.
Remember that render is called many times, and for each render you are creating 3 timeouts, and each timeout creates 3 more every time it's fired, so you end up with timeouts growing exponentially, and that will freeze your browser.

so this is the expected behaviour? well i thought i was able to use setTimeout in react but maybe it was not in render, just in in handler

yes, it's the same on React: only use setTimeout inside of an effect or event handler.