8

I'm currently working on a react component with a simple form (no redux, just using react-bootstrap for styling)

I'm pulling data from a database in the following form:

[
  {
    "id":"123",
    "name":"Amy Pond",
    "age":"22",
    "reputation":22000
  },
  {
    "id":"124",
    "name":"Clara Oswald",
    "age":"24",
    "reputation":35000
 }
 ...
]

and putting it into an object:

let userlist = [];
userlist = addUsers(this.state.users);

I used map to populate a dropdown based on this data, and can correctly set the state of the user to the selected one from the drop down:

<FormControl
   id = "user"
   componentClass="select"
   onChange={this.handleChange}
>
   {userlist.map((r , i) =>
     <option
       key={i}
       value={r.name}>
       {r.name}
     </option>
  )}
</FormControl>

using handleChange()

handleChange(event) {
  this.setState({
    value: event.target.value,
    // reputation: ???? // here's where I'm running into issues
    // age: ???????  // and here
});

My problem is, I need the user's age and reputation in the handleChange function to set their states accordingly. I've tried passing just r instead of r.name but I get [object Object] back.

console.log ("Handle Change Value: " + event.target.value);

if I try event.target.value.name I get undefined back.

The only thing that has sort of worked so far, is using JSON.stringify, but I feel like that's not the right way to do this. I've also thought about just searching through the objects once I've gotten just the name, but if there was a lot of objects I think that would be extremely inefficient? I'm also not sure how to do that.

Can anyone please help me find a way to pass these two extra values so I can set the state? Is map even the best way to create the options? I honestly feel like this is such a simple thing, but I'm still new to react and I've been struggling with this for far too long!

Any ideas / changes / comments / way to make this better would be much appreciated! I realize that perhaps using something like redux-form would make this easier, but I'm hoping there's a simple way to do it without that just for now.

Edit: onChange should have been set to onChange = {this.handleChange}, just pasted the wrong code from my attempts to troubleshoot. I'm binding this in the constructor:

constructor(props) {
  super(props);
  ...
  this.handleChange = this.handleChange.bind(this);
}
1
  • I have a small question regarding this same structure. When Im using it like: <Animal name = {animal.name} age = {animal.age} clicked = {this.removeAnimal.bind(this, i)} setName = {this.setAnimalName.bind(this, i)} index = {i}/> This whole stuff is within a js map method call's parameter arrow function. In the setAnimalName method I receive the 2nd object as the event object, is it even possible? If you have any idea pls help me Commented Jan 9, 2018 at 13:51

3 Answers 3

11

Use the user ID as the value in each option, then in handleChange find the user in your userlist to access all the user's properties:

<FormControl
   id = "user"
   componentClass="select"
   onChange={this.handleChange.bind(this)}
>
   {userlist.map((r , i) =>
     <option
       key={i}
       value={r.id}>
       {r.name}
     </option>
  )}
</FormControl>

handleChange(event) {
  const userId = event.target.value;
  const user = userlist.find(u => u.id === userId);
  this.setState({
    value: user
  });
}

Also, the way you are passing the onChange property might cause issues. It's hard to tell without seeing all your code, but make sure that you pass in a reference to your handleChange function (see above in my answer) rather than calling it right there (as in your question: onChange={this.handleChange()}, unless your this.handleChange function returns a function).

Sign up to request clarification or add additional context in comments.

3 Comments

This worked! Thank you :) Followup question: I'm making my axios call in componentWillMount(), and setting state like this.setState({ users: response.data, usersArray:addUsers(response.data) }); I need process the data with addUsers() to get only the data that I want. I'm currently doing userlist = addUsers(this.state.users); in render() because it seems like the raw data isn't received until then. in my handleChange, I can't access userlist, so have to use the state list const user = this.state.userArray.find(u => u.id === userId) Do you know how I can do this better?
You're right to make the axios call in componentWillMount(). When you get the response, I would transform your data right there before you add it to the component's state. Once it's in the state, you can access it both in the render method (to display the dropdown options) and in the handleChange function (to find the selected user by id).
I have a small question regarding this same structure. When Im using it like: <Animal name = {animal.name} age = {animal.age} clicked = {this.removeAnimal.bind(this, i)} setName = {this.setAnimalName.bind(this, i)} index = {i}/> This whole stuff is within a js map method call's parameter arrow function. In the setAnimalName method I receive the 2nd object as the event object, is it even possible? If you have any idea pls help me
2

It could be a problem with the way you've set your event listener, settings onChange={this.handleChange()} would cause the listener to fire right away. Using onChange={this.handleChange} correctly sets the listener.

<FormControl
   id = "user"
   componentClass="select"
   onChange={this.handleChange}>
   {userlist.map((r , i) =>
     <option
       key={i}
       value={r.name}>
       {r.name}
     </option>
  )}
</FormControl>

See here for more on events.

You can then use find() in your event handler to locate the user:

handleChange(event) {
  let user = users.find(u => u.name === event.user.name);
  this.setState({user: user});
}

1 Comment

Hmmm, when I try this with the correct {this.handleChange} I'm hitting the following error: TypeError: Cannot read property 'find' of undefined Any ideas?
1

you can use option tag attribute and json to encode/decode like this:

<FormControl
        id="user"
        componentClass="select"
        onChange={this.handleChange.bind(this)}
    >
        {userlist.map((r, i) =>
            <option
                key={i}
                value={r.id}>
                {r.name}
                data={JSON.stringify(r)}
            </option>
        )}
    </FormControl>;

    handleChange(event)
    {
        if (e.target.options && e.target.selectedIndex) {
            const user = e.target.options[e.target.selectedIndex].getAttribute('data');
            this.setState({
                value: user
            });
        }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.