In the chapter on Designing the State Shape, the docs suggest to keep your state in an object keyed by ID:
Keep every entity in an object stored with an ID as a key, and use IDs to reference it from other entities, or lists.
They go on to state
Think of the app’s state as a database.
I'm working on the state shape for a list of filters, some of which will be open (they're displayed in a popup), or have selected options. When I read "Think of the app’s state as a database," I thought about thinking of them as a JSON response as it would be returned from an API (itself backed by a database).
So I was thinking of it as
[{
id: '1',
name: 'View',
open: false,
options: ['10', '11', '12', '13'],
selectedOption: ['10'],
parent: null,
},
{
id: '10',
name: 'Time & Fees',
open: false,
options: ['20', '21', '22', '23', '24'],
selectedOption: null,
parent: '1',
}]
However, the docs suggest a format more like
{
1: {
name: 'View',
open: false,
options: ['10', '11', '12', '13'],
selectedOption: ['10'],
parent: null,
},
10: {
name: 'Time & Fees',
open: false,
options: ['20', '21', '22', '23', '24'],
selectedOption: null,
parent: '1',
}
}
In theory, it shouldn't matter as long as the data is serializable (under the heading "State").
So I went with the array-of-objects approach happily, until I was writing my reducer.
With the object-keyed-by-id approach (and liberal use of the spread syntax), the OPEN_FILTER
part of the reducer becomes
switch (action.type) {
case OPEN_FILTER: {
return { ...state, { ...state[action.id], open: true } }
}
Whereas with the array-of-objects approach, it's the more verbose (and helper function reliant)
switch (action.type) {
case OPEN_FILTER: {
// relies on getFilterById helper function
const filter = getFilterById(state, action.id);
const index = state.indexOf(filter);
return state
.slice(0, index)
.concat([{ ...filter, open: true }])
.concat(state.slice(index + 1));
}
...
So my questions are threefold:
1) Is the simplicity of the reducer the motivation for going with the object-keyed-by-id approach? Are there other advantages to that state shape?
and
2) It seems like the object-keyed-by-id approach makes it harder to deal with standard JSON in/out for an API. (That's why I went with the array of objects in the first place.) So if you go with that approach, do you just use a function to transform it back and forth between JSON format and state shape format? That seems clunky. (Though if you advocate that approach, is part of your reasoning that that's less clunky than the array-of-objects reducer above?)
and
3) I know Dan Abramov designed redux to theoretically be state-data-structure agnostic (as suggested by "By convention, the top-level state is an object or some other key-value collection like a Map, but technically it can be any type," emphasis mine). But given the above, is it just "recommended" to keep it an object keyed by ID, or are there other unforeseen pain points I'm going to run into by using an array of objects that make it such that I should just abort that plan and try to stick with an object keyed by ID?
See Question&Answers more detail:
os