pomber / didact

A DIY guide to build your own React
https://pomb.us/build-your-own-react/
6.29k stars 531 forks source link

useState + setTimeout = broken state setter #24

Closed shadowvzs closed 4 years ago

shadowvzs commented 4 years ago

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

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

pomber commented 4 years ago

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.

shadowvzs commented 4 years ago

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

pomber commented 4 years ago

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