Search code examples
javascriptreactjsselectstatedropdown

React dropdown / select not updating state


Updating state works if the state i am trying to update is outside the users array. But since i am having multiple users i need the state to be inside the objects and update anyway

I keep getting the error TypeError: Cannot read property 'name' of undefined

I've thought of setting state inside of a loop but i was told thats a bad idea.

So [e.target.name]: e.target.value was the only code i could find for dropdowns.

I tried passing id for each of the users but didnt know how to change state using that or what condition to put.

import React, { Component } from 'react'
export default class App extends Component {

  state = {
    users: [
      {
        id: uuid(),
        firstName: 'John',
        lastName: 'Doe',
        favColor: 'None'
      },
      {
        id: uuid(),
        firstName: 'Jane',
        lastName: 'Doe',
        favColor: 'None'
      }
    ]
  }

  handleChange = (e) => {
    this.setState({
      [e.target.name]: e.target.value
    })
  }

  render() {

    return (
      <div>
        {this.state.users.map((user) => {

          return <div key={user.id}>

            <h1>{user.firstName}</h1>
            <h1>{user.lastName}</h1>

            <form>
              <select 
                  name="favColor" 
                  value={user.favColor} 
                  onChange={() => this.handleChange(user.id)}
              >
                <option value="None" disabled>None</option>
                <option value="Blue">Blue</option>
                <option value="Red">Red</option>
                <option value="Green">Green</option>
              </select>
            </form>

            <h1>Fav Color: {user.favColor}</h1>
            <hr />
          </div>
        })}
      </div>
    )
  }

}

I expect the dropdowns to change state separately for each of the users


Solution

  • Your handleChange method is not accepting the correct arguments. If you wish to update one user item in array you will need to create a new updated copy of the array and save back into state

    handleChange = (e,id) => {
    const updatedUser = {...this.state.users.find(x=>x.id ===id), favColor: e.target.value}
     this.setState({
        users:  [...this.state.users.filter(x==>x.id!==id),updatedUser]
        })
    }
    
    ...
    onChange={(e) => this.handleChange(e,user.id)}
    

    To simplify mutations of state I can recommend taking a look at Immer

    And as @JosephD rightly pointed out this won't mantain order so you will need to do something like this.state.users.map(u => u.id === id ? { ...u, favColor: e.target.value } : u)

    Here is a codesandbox based on your code: https://codesandbox.io/s/loving-cohen-do56l?fontsize=14