React.setState doesn't update as expected in useHook

55 views Asked by At

I have a React hook:

const useWidgets = () => {
    const [skip, setSkip] = useState(0);
    const take = 20;

    async function getItems() {
        try {
            console.log('skip is always 0 here', skip);

            const rsp = await apiGetWidgets({ skip });
            setSkip((state) => state + take);
        } catch (err) {
            console.log(err);
        }
    }

    useEffect(() => {
        getItems();
    }, []);

    return {  getItems, skip };
};

And I have a functionalComponent, that consumes the hook:

const Infinite: FC = () => {
    const { getItems, skip } = useWidgets();

    
    useEffect(()=> {
        setTimeout(getItems, 4000)
    }, [])
    
    console.log('skip is updated here to latest value', skip);

    return <div>
        {skip}
    </div>
};

When skip is updated by setSkip the value change is updated in the functional Component but that change is not reflected in the useWidget hook, skip is always zero.

Is this because the useWidgets hook closed over the initial value of skip which starts out at zero?

I thought any subsequent calls to getItems would be able to access the new value of skip but it remains at zero. Every time the functional component Infinite gets re-rendered I thought a new call to useWidgets would be called and the latest value of skip would be maintained, but I guess it reinitializes at zero?

Can anyone explain the behavior of why I cant access current state in the useWidgets hook, but the functionalComponent Infinite can?

1

There are 1 answers

7
Gabriele Petrioli On BEST ANSWER

No, the closure is not happening inside the useWidgets but it is happening inside the useEffect of the Infinite component.

The getItems in that useEffect is the one from the initial render of the component, so it uses the "first" defined getItems. It just delays its execution due to the timeout.

If you add a button inside the Infinite component that invokes the getItems you will see that it is getting the correct one.

See demo at https://stackblitz.com/edit/af-react-sandbox-d5spdh?file=index.js