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.