Gaurang Shah
Gaurang Shah

Reputation: 12910

ReactJS event handler update state of dictionary value

I am trying to write a event handle for few input box and I realize that it's not able to update the state of the dict. if I change it to string it works fine.

if I change state to following it works fine.

this.state = {          
        firstName: "",
        lastName: ""    
}

However following doesn't

import React, {Component} from "react"

class App extends Component {
    constructor() {
        super()
        this.state = {
            list: {
                firstName: "",
                lastName: ""    
            }

        }
        this.handleChange = this.handleChange.bind(this)
    }

    handleChange(event) {
        const {name, value} = event.target
        console.log(name)
        this.setState({
            [name]: value
        })
    }

    render() {
        return (
            <form>
                <input 
                    type="text" 
                    value={this.state.firstName} 
                    name="list[firstName]" 
                    placeholder="First Name" 
                    onChange={this.handleChange} 
                />
                <br />
                <input 
                    type="text" 
                    value={this.state.lastName} 
                    name="list[lastName]" 
                    placeholder="Last Name" 
                    onChange={this.handleChange} 
                />

                <h1>{this.state.firstName} {this.state.lastName}</h1>
            </form>
        )
    }
}

export default App

Upvotes: 2

Views: 5562

Answers (3)

Sahil Raj Thapa
Sahil Raj Thapa

Reputation: 2483

The second case does not work because there are flaws. So to make your code run you need to make two changes in your input field

1) name="list[firstName]" as name="firstName"

2) value={this.state.firstName} as value={this.state.list.firstName}

If you use name="list[firstName]" in your input field then whenever [name]: value in handleChange method executes, it evaluates to ['list[firstName]']: value and it will create another property list[firstName] in the state.

i.e state = { list: {...}, list[firstName]: value }.

So it won't update the property firstName inside the list as you expect.

For more detail: Computed Property Names

And using value={this.state.list.firstName} we can map state list.firstName with the input field

    <input 
            type="text" 
            // do not use value={this.state.firstName} 
            value={this.state.list.firstName} 
            // do not use name="list[firstName]"
            name="firstName" 
            placeholder="First Name" 
            onChange={this.handleChange} 
    />

    <input 
            type="text" 
            value={this.state.list.lastName} 
            name="lastName" 
            placeholder="Last Name" 
            onChange={this.handleChange} 
    />

In your handleChange method, your are trying to update the property firstName and lastName inside list.

So to do that first you need to use list inside this.setState method as this.setState({ list: {...}}).

As list is an object and you want to update specific property of list so first you need to copy all properties inside the list using spread operator. And then after that you can change the property you want to change using dynamic / computed property. So change your handleChange method to

handleChange(event) {
        const {name, value} = event.target
        this.setState({
            list: {
              // copy all properties inside the "list" 
              // so that we change only the property 
              // we need to change and keep other properties as it is
              ...this.state.list,
              // dynamically changing property
              [name]: value
            }
        })
    }

Upvotes: 2

Umair Farooq
Umair Farooq

Reputation: 1823

In your handleChange function you can change setState to following:

this.setState({
      list: {
        [name]: value
      }
    })
// in input
value={this.state.list.firstName} 

Upvotes: 2

Blundering Philosopher
Blundering Philosopher

Reputation: 6805

First, you're correctly destructuring the name and value props from event.target in your handleChange function, BUT the name properties you set on your two <input> elements are not intuitive. Your name properties are currently "list[firstName]" and "list[lastName]" -> this won't reach into your this.state.list[firstName] / this.state.list[lastName] properties as you wish - instead, you should change your name properties to reflect your state values, like this:

<input
    name="firstName"
    {/* other props stay the same... */}
/>
<input
    name="lastName"
    {/* other props stay the same... */}
/>

Now that your <input> elements have name properties that also match values on your state, you can change your handleChange function to something like this:

handleChange(event) {
    // get name and value properties from event target
    const {name, value} = event.target
    this.setState(prevState => ({
        // update your 'list' property
        list: {
          // spread old values into this object so you don't lose any data
          ...prevState.list,
          // update this field's value
          [name]: value
        }
    }))
}

Upvotes: 2

Related Questions