React: Calling setState in the callback of setState prevents input's value to be updated by onChange

Created on 7 Sep 2019  路  5Comments  路  Source: facebook/react

I am reporting a bug

Current behavior: Calling setState in the callback of setState to generate dynamic HTML containing inputs prevents input's value= to be updated by onChange=.

Concretely, for a use case like this:

render() {
    return (
        <div>
            <div>{this.state.inputs}</div>
            // <button>Add one more input</button>
        </div>
    )
}

This doesn't work:

var apiData = [{ value: "" }, { value: "" }]
this.setState({
    items: apiData,
}, () => this.setState({
    inputs: apiData.map((v, i) => {
        return <input key={i} value={this.state.items[i].value}
            onChange={(e) => this.handleChangeArray(e, i)} />
    })
}))

Here is a basic example that shows bug: https://jsfiddle.net/jw81uo4y/1/

I opened a question on stack overflow to check with the community first if I was not doing something silly.

_I'm running [email protected]_

Thanks!

Most helpful comment

While it may not be directly related to your issue, my first observation is that you generally shouldn't be storing React elements directly in your state. Instead, you should store just the _data_ in your state, and re-render based on that data.

All 5 comments

While it may not be directly related to your issue, my first observation is that you generally shouldn't be storing React elements directly in your state. Instead, you should store just the _data_ in your state, and re-render based on that data.

I think it is not a bug. You are not updating your items when value changes, and items' value are bound to this.state.items[i].value. If you want to go this way further (which I do not recommend) you have to update inputs inside handleChangeArray.

Edit:
I made updates in your fiddle so you can see what I mean https://jsfiddle.net/q5fpw8no/1/ .

But please note that this way of implementing components may lead to another issues, is not intuitive for react devs, violates react rules. You shouldn't keep components in your state.

is not intuitive for react devs, violates react rules.

Can you elaborate? What rules does this violate?

is not intuitive for react devs, violates react rules.

Can you elaborate? What rules does this violate?

  • State is for data, not view itself. You shouldn't create components into state - that's what render is for.
  • React optimizes rendering in many ways, creating components outside render context we may loose those advantages,
  • When working with state, we should create optimized code, based on this example
    this.setState({ items: apiData, }, () => this.setState({ inputs: apiData.map((v, i) => { return <input key={i} value={this.state.items[i].value} onChange={(e) => this.handleChangeArray(e, i)} /> }) }))
    this have potential to be too slow

I should call these best practices instead of rules but back then I couldn't find better word 馃槂

Thank you for your feedback.

What I was doing looked really logical to me until with your feedbacks and more research I found out that this.setState() doesn't work as I expected on object's properties and React elements.

My reasoning was that "if it is dynamic, then it should be in the state". Not sure if this could be made more clear in the intro to react.

@pmusiala I see your edits, it makes sense to me only because I did more research the last couple days. If I saw that a few days ago, I would be thinking "Why? It is so dumb, items is already in the state, why would I regerenate all my inputs?"

Anyway, I was trying to optimize (avoid a copy of data later) and now I do realize it is kind of a beginners mistake, and that it would actually have made things slower.

Thanks guys!

Was this page helpful?
0 / 5 - 0 ratings