anonym anonym - 9 months ago 36
React JSX Question

Combining year, month and day to create date in ReactJS

I have a form which has select dropdowns for year, month and day.

<select onChange={(event) => this.mergeDate('day',}>
<option value="1">1</option>

<select onChange={(event) => this.mergeDate('month',}>
<option value="1">January</option>

<select onChange={(event) => this.mergeDate('year',}>
<option value="1977">1e977</option>

I need to create date out of these values and store them in the
field of state. In order to do that, I created three additional properties as

this.state = {
name: '',
email: '',
birthday: '',
country: '',
date_day: '',
date_mth: '',
date_year: ''

And on the select dropdown change, I created a function that merges the values using
function as follows:

mergeDate(type, value)

if (type === 'year') { this.setState({ date_year: value }) }
if (type === 'month') { this.setState({ date_mth: value }) }
if (type === 'day') { this.setState({ date_day: value }) }

console.log('merging date now '); // --> Works!

let newDate = new Date(this.state.date_year, this.state.date_mth, this.state.date_day);

console.log(newDate); // --> successful, but shows date value of last onChange event

this.setState({ birthday: newDate });

console.log(this.state); // --> {...., birthday: '', ....}


In this function, the second console.log shows the date set on last onChange event. The last console.log logs empty value in the
. I'm guessing the problem is because of
asynchronous behavior
which I do not yet understand. Can anyone please explain to me the reason behind and solution to the problem?



According to Asad,
and any action to be done after setting the state is complete should be passed as a parameter to the setState function. I tried:

let newDate = new Date(this.state.date_year, this.state.date_mth, this.state.date_day);
this.setState({ birthday : newDate });

mergeDate(type, value)
if (type === 'year') { this.setState({ date_year:value }, this.postUpdate(newDate))}
if (type === 'month') { this.setState({ date_mth: value }, this.postUpdate(newDate))}
if (type === 'day') { this.setState({ date_day: value }, this.postUpdate(newDate))}

This is still giving me the same output - shows date value of last onChange event. What am I missing?



The solution is to not pass the function alone but as a function.

mergeDate(type, value)
if (type === 'year') { this.setState({ date_year:value }, () => this.postUpdate())}
if (type === 'month') { this.setState({ date_mth: value }, () => this.postUpdate())}
if (type === 'day') { this.setState({ date_day: value }, () => this.postUpdate())}

Answer Source

setState is asynchronous, as React often batches state updates for performance. This means you can't rely on fresh state being available in statements following setState. If you rely on ordering of changes, you should pass a callback, like so:

mergeDate(type, value)
        function postUpdate() {
            // All of the code from after your if statements
        if (type === 'year')  { this.setState({ date_year: value }, postUpdate) }
        if (type === 'month') { this.setState({ date_mth: value  }, postUpdate) }
        if (type === 'day')   { this.setState({ date_day: value  }, postUpdate) }


In general though, you should simply be doing all your computations up front and just calling setState once at the end of the function with all your desired changes to the state.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download