Shh
Shh

Reputation: 309

React JS accessing an array nested in state

class CreateEventForm extends Component {
  constructor(props) {
    super(props)
    this.state = {
      fields: {
        name: '',
        location: '',
        description: '',
        datetime: '',
      },
      friendsInEvent: [],
    },
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    const { checked, value } = e.target
    let { friendsInEvent } = { ...this.state }
    if (checked) {
      friendsInEvent = [...friendsInEvent, value]
    } else {
      friendsInEvent = friendsInEvent.filter(el => el !== value)
    }
    this.setState({ friendsInEvent }, () => console.log(this.state))
  }

  render() {

    const { friends, createEvent, addFriendsToEvent } = this.props
    const { fields: { name, location, description, datetime } } = this.state

    const setter = propName => event => {
      const nextFields = { ...this.state.fields }
      nextFields[propName] = event.target.value
      this.setState({ fields: nextFields })
    }

    return (
      <div {...cssForm}>
        <div>
          <Label label="Event Name" />
          <Field onChange={setter('name')} />

          <Label label="Event Date/Time" />
          <Field onChange={setter('datetime')} type="datetime-local" />

          <Label label="Event Location" />
          <Field onChange={setter('location')} />

          <Label label="Event Description" />
          <Field onChange={setter('description')} />

            <SubHeader title="Add Friends to the Event..." />
              <div>
                {friends
                  .mapEntries(([friendId, frn]) => [
                    friendId,
                    <div key={friendId}>
                      <input
                          value={friendId}
                          type='checkbox'
                          onChange={this.handleChange}
                          defaultChecked={false} />
                      {frn.firstName} {frn.lastName}
                    </div>,
                  ])
                  .toList()}
              </div>

            <Link to="/">
                <Button style="blue" name="Done" onClick={() => createEvent(this.state.fields)} />
            </Link>
        </div>
      </div>
    )
  }
}

export default CreateEventForm

This code above works and stores all of the correct values as expected. However I want to move friendsInEvent[ ] inside fields{ }. To look like:

super(props)
 this.state = {
  fields: {
    name: '',
    location: '',
    description: '',
    datetime: '',
    friendsInEvent: [],
  },   
},
  this.handleChange = this.handleChange.bind(this);
}

How can I achieve this? Everything I have tried breaks the handleChange(e) function or overwrites fields{ } with the friendsInEvent array.

I assume I have to change the values inside the handleChange(e) function after I move the array inside this.state.field?

Also, is there a way to merge my two functions setter and handleChange(e)? I kept them separate as they handle two different types (string and array).

Thanks.

Upvotes: 1

Views: 3897

Answers (2)

Hemadri Dasari
Hemadri Dasari

Reputation: 33974

Why can't you do it in the below way.

handleChange(e) {
    const { checked, value } = e.target
    let { friendsInEvent } = { ...this.state }
    if (checked) {
      friendsInEvent = [...friendsInEvent, value]
    } else {
      friendsInEvent = friendsInEvent.filter(el => el !== value)
    }
    let object = {};
    object.name = this.state.name;
    object.location = this.state.location;
    object.description = this.state.description;
    object.datetime = this.state.datetime;
    object.friendsInEvent = friendsInEvent;
    this.setState({
        fields: object
    })
  }

Never do setState inside render. You are not suppose to do setState inside render. Event handlers should be handled before render method but not inside of render. Try avoiding doing setState inside render.

const setter = propName => event => {
      const nextFields = { ...this.state.fields }
      nextFields[propName] = event.target.value
      this.setState({ fields: nextFields })
    }

Upvotes: 0

Andy
Andy

Reputation: 63524

From the looks of it you'll need to make the following changes to handleChange:

handleChange(e) {
  const { checked, value } = e.target;

  // Ensure that you're destructuring from fields
  let { friendsInEvent } = { ...this.state.fields };
  if (checked) {
    friendsInEvent = [...friendsInEvent, value];
  } else {
    friendsInEvent = friendsInEvent.filter(el => el !== value)
  }

  // You need to take a copy of state, and then supply a new copy
  // of the the fields property which takes a copy of the old fields
  // property, and overwrites the friendsInEvent with the new data
  this.setState({
    ...this.state,
    fields: {...this.state.fields, friendsInEvent }
  }, () => console.log(this.state));
}

Here's a small React-free example of how this would work.

Upvotes: 1

Related Questions