3

I am developing a React web app. Until now, I always stored data in an array like such:

areas: [
    { id: 0, title: 'Some title', isSelected: false }
]

Then I could just do

this.state.areas.map(x => <Component key={x.id} ... />

However after going over the Redux docs, I see an advantage is storing data like so:

areas: {
  'some title': { id: 0, isSelected: false }
}

This makes it super easy to update the currently selected area by doing

this.state.areas['some title].isSelected = true

As opposed to doing .filter by the id, then updating isSelected.

However, storing using object keys instead of an array means I have to write:

 { 
   Object.entries(this.props.areas).map(([k, v]) => {
     return <Component key={k.id} ... />
   })
 }

Which is a lot less readable, and a lot more typing.

So my question is:

  1. Is there a nicer way than Object.entries... to achieve what I'm doing? Or is there a better way?
  2. Should I just be using an array in the first place?

Thanks.

lmiller1990
  • 925
  • 2
  • 12
  • 22
  • 1
    You're not actually doing `this.state.areas['some title].isSelected = true`, are you? You should _never_ mutate state outside of using `this.setState` or via an action. – monners Sep 13 '17 at 01:38

2 Answers2

5

From your question, it seems like you are confused when to store values in array and values as object in react state. And it depends on your requirement.

  • store in array, if data is used only for display purpose and not going to be changed using any event. like, to render dynamic options in select box. You can store values as array and can use 'Array.map' to create component array and render it.

  • Objects are generally used when you are changing data / update data on events. Objects are handy when it comes to update specific value inside it. like, you are rendering table row with checkbox in first column and on checkbox mark/unmark you need to update related row data property.

If your application requires both things to be achieved, you should do as following,

{
    areas: ['areaid_0', 'areaid_1', 'areaid_2'],
    areaDetails: {
        'areaid_0': {title: 'Some title', isSelected: false},
        'areaid_1': {title: 'Some title', isSelected: false},
        'areaid_2': {title: 'Some title', isSelected: false}
    }
}

When it comes to rendering you can do like,

this.state.areas.map(x => {
  const areaDetail = this.state.areaDetails[x];
  <Component key={x} {...areaDetail}... />
})

When you wants to update any object, you can do like,

this.state.areaDetails[x].isSelected = true;

Hope, this will be helpful to you.

Ankit Parikh
  • 116
  • 1
  • 3
  • Great! I think you are right - I need to make a more careful choice about when to use objects and arrays, this was really useful. – lmiller1990 Sep 13 '17 at 05:56
5

Storing objects in a sort of "ID map" like this is something that is often recommended by the Redux docs. Note, the keys are the IDs by convention, not an arbitrary attribute like title.

While you can indeed use the Object.entries style that you have demonstrated, you are almost always going to want to have some sort of logical sort order.

You should not rely on JavaScript retaining sort order of keys, and for this reason, libraries like normalizr (which Abramov recommends) that automatically convert to this normalized structure will also return a result array of IDs that will retain sort order. Reference Here

The "Normalizing State Shape" docs from Redux also explicitly recommend you rely on an array of IDs for sort order:

  • Any references to individual items should be done by storing the item's ID.
  • Arrays of IDs should be used to indicate ordering.

Your state shape might look like this:

{
  areas: {1: {id: 1, title: 'some title'}, 2: {id: 2, title: 'other title'}},
  areaIds: [1, 2],
}

You would then just iterate like this:

areaIds.map(id => <Component key={id} area={areas[id]} />)

If you truly want to do the Object.entries way, you can use a library like lodash which lets you do simply:

_.map(areas, area => <Component key={area.id} area={area} />)
Rob Wise
  • 4,930
  • 3
  • 26
  • 31
  • Hi, thanks for the comment. That does help a lot. I found the other answer to be more relevant so I marked it as correct, but your input is also valuable. Thanks a lot. – lmiller1990 Sep 13 '17 at 05:56