'React - ref Value In onChange in map() That Generate <table> (Meteor)

here is a snippet from render()

{users.map((user)=>{
    return <tr key={user._id}>
        <td><input  onChange={this.handleChange.bind(this, user._id)}
            type="text" 
            ref="name" 
            value={user.profile.firstName} />
        </td>
    </tr>
})}

here is the handler:

handleChange(userId) {
    console.log(userId);
    var name = this.refs.name.value.trim();
    console.log(name);
}

The handler will print the correct userId but always the last name in the user table

What is the correct way to create a handler that can fetch the values of the specific input field in a table created by map?

Should I generate unique refs somehow?

Or is there a different method for this one?



Solution 1:[1]

onChange receives a SyntheticEvent as a parameter as well. If you add that to your onChange handler, you can inspect the target and get the new value:

handleChange:

handleChange(userId, event) {
    console.log(userId);
    var inputElement = event.target;
    console.log(inputElement.name + ': ' + inputElement.value);
}

To handle many inputs in a single handleChange, add standard name attributes to the inputs and inspect them in the change handler:

{users.map((user)=>{
    return <tr key={user._id}>
        <td><input onChange={this.handleChange.bind(this, user._id)}
            name="name"
            type="text"
            value={user.profile.firstName} />
        </td>
    </tr>
})}

This is covered in the controlled components docs.

Solution 2:[2]

Create new component UserListRow.jsx move the code there, e.g.:

                    {users.map((user)=>{
                        return <UserListRow user={user} key={user._id}/>
                    })}

UserListRow.jsx render:

return <tr key={user._id}>
        <td><input  onChange={this.handleChange.bind(this, user._id)}
            type="text" 
            ref="name" 
            value={user.profile.firstName} />
        </td>
    </tr>

move handleChange to UserListRow.jsx

Solution 3:[3]

I found a solution using the index. You can create an object containing the information you want to update and the item index and set this object on state. Then, on input's value you make a verification between the item index and the object's index you set on state.

here follows and example in typescript and hooks.

interface userNameState {
  index: number;
  name: string;
}

const [userName, setUserName] = useState({} as userNameState);

the handle change function.

function handleChange(e: FormEvent, index: number) {
    let enterEvt = e.target as HTMLButtonElement

    const userName = {
      index: index,
      title: enterEvt.value
    }
    setUserName(userName)
}

and the map function would something like that

{users.map((user, index)=>{
    return <tr key={user._id}>
        <td>
          <input  
            onChange={(e) => handleChange(e, index)}
            type="text"
            value={userName.index === index ? userName.name : user.name} />
        </td>
    </tr>
})}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Elia Weiss
Solution 3 Thiago Padovani