0

Consider this JSON structure, which is returned by a server AJAX request:

{
  "Items": [
  {
    "ID": 1,
    "Name": "John Smith"
  },
  {
    "ID": 2,
    "Name": "Jane Doe"
  }
]}

And a Fetch function that sets the JSON data into the state of a view component:

FetchData(json => {
  this.setState({
    Items: json.Items,              
  })
})

Further down the component tree there is a React component with a checkbox, whose render() method looks like this:

render(){
  return (
    <input type="checkbox" checked={this.props.Item.IsSelected} onClick={this.handleToggleItemEvent.bind(this)} />
    <span>{this.props.Item.Name}</span>
  );
}

The main view component has a handleToggleItemEvent that is responsible for setting the IsSelected state of the item:

handleToggleItemEvent(ID){
  var item = _.find(this.state.Items, item => {
    return item.ID === ID;
  });

  if(item){
    item.IsSelected = !item.IsSelected;
    this.setState({
      items: this.state.Items
    });
  }
}

If the checkbox for the first item is clicked, the object will look like this:

{
  "Items": [
  {
    "ID": 1,
    "Name": "John Smith",
    "IsSelected": true
  },
  {
    "ID": 2,
    "Name": "Jane Doe"
  }
]}

Because the IsSelected state is internal to the application and not returned from the server, when I fetch the data again (say by filtering the data), the IsSelected property will go away. What is the best way to merge this data together (or is this just a bad idea entirely)? I have seen the Immutability Helpers, but I am not sure how I can use them to merge properties for each element in the array.

1 Answer 1

1

I don't think keeping the IsSelected within the model is the best approach, as IsSelected holds a view/controller-specific value, while the rest of the properties are model-specific.

You can change your model and add a selectedItems dictionary and keep the id's there (this also simplifieshandleToggleItemEvent):

handleToggleItemEvent(ID){
    var selectedItems = this.state.selectedItems;
    selectedItems[ID] = true;
    this.setState({selectedItems: selectedItems})
}

You'd also have to update the render method to check the checkboxes based on state.selectedItems.

I'd also recommend initialising it in getInitialState():

getInitialState() {
    return {selectedItems:{}};
}
Sign up to request clarification or add additional context in comments.

4 Comments

I did consider the separate array, though it didn't seem quite right because it means I need to merge the two "states" in the render() function and anywhere else I want to get the currently selected items.
Think at this: what if later on, for some reason, suddenly, the objects received from server have a IsSelected attribute? You'd overwrite it and introduce bugs in your app. The selected status is part of your app, and not part of the objects received from server, so it makes sense to store it separately.
I think that makes sense for one type of state, but I have other app states that will be related to these objects as well like IsExpanded (referring to a view details links where you can see more information about the item). So Now I'll need this.state.selectedItems, this.state.expandedItems, etc. and all of these need to be passed down as props. What if I stored the client app state in a separate object that is still attached to the server object? So I could access it like item.appState.IsSelected, item.appState.IsExpanded? Should prevent collisions.
Well, you can use an appState dictionary: appState = {ID1: {selected: true, expanded:false}}

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.