Shh
Shh

Reputation: 309

React JS Storing an array of ids in state

I have a prop called friends which is being listed as a checkboxes (default: unselected).

Example output:

o Bob Smith

o Tom Brown

How can I save the id's of all the names that are selected?

E.g. both are ticked/selected -> ids 1 and 2 is stored.

This is what I have so far:

class SelectFriends extends Component {
  constructor(props) {
  super(props)
  this.state = {
    SelectedFriendsIds: [],
  }
}

render() {
  const { SelectedFriendsIds } = this.state
  const { userId, friends, addFriendsTo } = this.props

return (
    <div>
      <SubHeader title="Add Friends..." />
      <div>
        {friends
          .mapEntries(([friendId, frn]) => [
            friendId,
            <div key={friendId}>
              <input
                  value={friendId}
                  type='checkbox'
               // onChange={ () => console.log("do something")
                  defaultChecked={false} />
              {frn.firstName} {frn.lastName}
            </div>,
          ])
          .toList()}
      </div>
        <div>
          <Button style="blue" name="Done" onClick={() => addFriendsTo(SelectedFriendIds, userId)} />
        </div>
    </div>
  )
 }
}

export default SelectFriends

Upvotes: 0

Views: 8499

Answers (5)

Andy
Andy

Reputation: 63524

Here's a slightly simplified version of your code with a new function handleChange that is called every time a checkbox is checked/unchecked.

We take a copy of the state and extract selectedFriendsIds. If an id is to be added to the state setState is called with a new array of the new value merged into selectedFriendsIds. If checkbox is unchecked the setState is called with a filtered copy of selectedFriendsIds instead.

You might want to run this snippet full page as it logs the state to the console after each change to show you the output.

class SelectFriends extends React.Component {

  constructor(props) {
    super(props);
    this.state = { selectedFriendsIds: [] }
    this.handleChange = this.handleChange.bind(this);
  }

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


  render() {
  
    const { friends } = this.props;
  
    return (
      <div>
        <h2>Add Friends</h2>
          {friends.map(({ friendId, frn }) => {
            return (
              <div key={friendId}>
                <input value={friendId} type="checkbox" onChange={this.handleChange} />{frn}
              </div>
            );
          })}
      </div>
    )
  }
}

const friends = [
  { friendId: 1, frn: 'Bob' },
  { friendId: 2, frn: 'Davros' },
  { friendId: 3, frn: 'Daisy' }
];

ReactDOM.render(
  <SelectFriends friends={friends} />,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>

Also available in a jsfiddle.

Upvotes: 2

Robert Jesionek
Robert Jesionek

Reputation: 1

I would like to suggest a small improvement to the design of your component, which also resolves the problem of how to maintain state of selected friend IDs efficiently. Frankly, there is no need to maintain state of selected Ids. Instead, please maintain a state of friends collection - where each object representing a friend, has 'selected' property. This way, You'd be able to easily filter selected ones and send this information to upper component. Below is the code snippet explaining this fully:

import React, { Component } from 'react';
import * as _ from 'lodash';
import PropTypes from 'prop-types';

class FriendsList extends Component {

state = {
    friends: []
}

constructor(props) {

    super(props);

    this.onAdd = this.onAdd.bind(this);

    this.state.friends = [ {id: 1, name: 'John', selected: false}, 
        { id: 2, name: 'Ally', selected: false},
        { id: 3, name: 'Becky', selected: false},
        { id: 4, name: 'Frank', selected: false},
        { id: 5, name: 'Mark', selected: true},
        { id: 6, name: 'Alice', selected: true}
    ];
}

onCheckboxChange(event, n) {
    n.selected = !n.selected;
    this.forceUpdate();
}

onAdd() {
    const selectedFriends = _.filter( this.state.friends, (friend) => {
        return friend.selected === true;
    });
    this.props.onAdd(selectedFriends);
}

render() {
    const { friends } = this.state;
    return (
        <div>
            {friends.map(n => {
                return (
                    <div key={n.id}>
                        {n.name} <input type="checkbox" onChange={(event) => this.onCheckboxChange(event, n)} checked={n.selected}/>
                    </div>
                );
            })}
            <button onClick={this.onAdd}>Add Friends</button>
        </div>
    );
}
}
FriendsList.propTypes = {
  onAdd: PropTypes.func,
};
export default FriendsList;

Upvotes: 0

Shubham Khatri
Shubham Khatri

Reputation: 281626

You need to check if the value is already in your selected list and add it if its not there else remove the value onChange event of the checkbox. Check the sample code below

class SelectFriends extends Component {
  constructor(props) {
  super(props)
  this.state = {
    SelectedFriendsIds: [],
  }
}

onChange = (val) => {
   const SelectedFriendsIds = [...this.state.SelectedFriendsIds];
   const presentIndex = SelectedFriendsIds.indexOf(val)
   if(presentIndex > 0) {
      SelectedFriendsIds.splice(presentIndex, 1);
   } else {
      SelectedFriendsIds.push(val);
   }
   this.setState({SelectedFriendsIds})

}
render() {
  const { SelectedFriendsIds } = this.state
  const { userId, friends, addFriendsTo } = this.props

return (
    <div>
      <SubHeader title="Add Friends..." />
      <div>
        {friends
          .mapEntries(([friendId, frn]) => [
            friendId,
            <div key={friendId}>
              <input
                  value={friendId}
                  type='checkbox'
                  onChange={ () => this.onChange(friendId)
                  defaultChecked={false} />
              {frn.firstName} {frn.lastName}
            </div>,
          ])
          .toList()}
      </div>
        <div>
          <Button style="blue" name="Done" onClick={() => addFriendsTo(SelectedFriendIds, userId)} />
        </div>
    </div>
  )
 }
}

export default SelectFriends

Upvotes: 1

Chaitanya Mankala
Chaitanya Mankala

Reputation: 1704

the code can be modified like this

  class SelectFriends extends Component {
    constructor(props) {
        super(props)
        this.state = {
            SelectedFriendsIds: {},  //CHANGING INTO OBJ FOR EASIER MANIPULATION
        }
    }

    render() {
        const { SelectedFriendsIds } = this.state
        const { userId, friends, addFriendsTo } = this.props

        return (
            <div>
                <SubHeader title="Add Friends..." />
                <div>
                    {friends
                        .mapEntries(([friendId, frn]) => [
                            friendId,
                            <div key={friendId}>
                                <input
                                    checked={friendId}   //USE CHECKED ALWAYS FOR CHECKBOXES
                                    type='checkbox'
                                    onChange={(e) => {
                                        let x = SelectedFriendsIds;
                                    if (e.target.checked) {
                                        x[friendId] = true;
                                    }
                                    else {
                                        delete x[friendId];
                                    }
                                    this.setState({
                                        SelectedFriendsIds: x
                                    })
                                    }} />
                                {frn.firstName} {frn.lastName}
                            </div>,
                        ])
                        .toList()}
                </div>
                <div>
                    <Button style="blue" name="Done" onClick={() => addFriendsTo(SelectedFriendIds, userId)} />
                </div>
            </div>
        )
    }
}

export default SelectFriends

If you need array of selected friends, just use

Object.keys(this.state.SelectedFriendsIds)

Upvotes: 0

Mos&#232; Raguzzini
Mos&#232; Raguzzini

Reputation: 15821

You can simply use array.push for adding ids on check array.filter to retrieve single id and array.slice to remove single id.

Upvotes: 0

Related Questions