Home > front end >  React state variable value appearing to be null
React state variable value appearing to be null

Time:08-06

I'm trying to access a value of my state variable, but it appears to be null when I change the date, but If I change any other input field, I don't get it null Here is the link to stackblitz running app.

If you open the console and type something in any field you can check the value of filter variable to be same as what you type, but if you click on the date range input, it will show you null, I'm scratching my head for hours around this. Thanks in advance to helping hands

CodePudding user response:

react-bootstrap-daterangepicker looks like abandonware, I'd pick a different library.

If you want to keep using it, note that the onEvent callback isn't updated when your component re-renders. So to get the latest state, use the setState callback:

    const onDateChange = (event, picker) => {
        ...
        setFilter(filter => {
            console.log('new filter', filter);
            return {...filter, hi: 'bye'};
        });
    }

This will let you access the latest filter state of your component even though the callback has closed over the stale initial render state.

The very first time your component renders, it runs the entire body of the function component. So it creates the function onDateChange on the first render. This function has access to the filter variable through a Javascript closure. You should familiarize yourself with closures if you aren't already. One way to think about it is your onDateChange has access to the parent variable scope where filter lives, even after the component has finished rendering. onDateChange has "closed over" the outer scope and can access the filter variable through the closure.

Every time your functional component "renders", like when you change state, the entire body of the component's function re-runs. In this case, it creates an entirely new onDateChange function, which will close over the latest version of the filter variable from state. So in theory, the new onDateChange function reference, when called by the internal guts of the Datepicker library, should have access to the latest filter state.

Apparently, what's happening instead, is the Datepicker library is only using the onDateChange from the first render, and not updating it when you pass in the newer created onDateChange. One reason a library might choose to do this is to avoid removing and re-attaching event listeners every time a render occurs, which could be important if there are lots of child elements that need to re-render. That's probably not what's happening with this Datepicker library, it's probably just a poorly designed and outdated library.

So your Datepicker renders once with the initial onDateChange function that's closed over your initial filter state. Every time your component renders, a new onDateChange closed over a new filter variable is created, but the Datepicker doesn't use the latest one, it uses the stale initial onDateChange one. The closure over the filter state isn't updated when your component re-renders, instead and entirely new onDateChange is made each render. The initial onDateChange is still around in memory and has access to the initial filter variable in memory.

If you were to call setFilter({...filter, new: 'value' }) inside the callback, every time it runs, that filter variable would be the stale initial value from your initial component render. The filter variable never gets updated inside the stale onDateChange function. It would keep overwriting your filter with null.

For problems like this, React provides a callback to the setState function that will always have access the latest state. So instead of

setFilter({ ...filter, newData })

you can do

setFilter(latestFilterState => { return {...latestFilterState, newData  } })

and React will call that inner callback function, setting latestFilterState to the latest value of the state. This works even though setFilter is also "stale" from the first render.

  • Related