When writing a react-redux
application, I need to keep both application and UI state in the global state tree. What is the best approach to design it's shape?
Lets say I have a list of todo items:
{
items: [
{ id: 1, text: 'Chew bubblegum' },
{ id: 2, text: 'Kick ass' }
]
}
Now I want to let the users select and expand the items. There are (at least) two options how to model the state shape:
{
items: [
{ id: 1, text: 'Chew bubblegum', selected: true, expanded: false },
{ id: 2, text: 'Kick ass', selected: false, expanded: false }
]
}
But this is mixing the UI state (selected
and expanded
) with the application state. When I save the todo list to the server, I want to save just the application state, not the UI state (in real-world app, UI state can contain state of modal dialogs, error messages, validation status etc).
Another approach is to keep another array for the UI state of the items:
{
items: [
{ id: 1, text: 'Chew bubblegum' },
{ id: 2, text: 'Kick ass' }
],
itemsState: [
{ selected: true, expanded: false },
{ selected: false, expanded: false }
]
}
Then you have to combine those two states when rendering an item. I can imagine that you can zip
those two arrays in the connect
function to make rendering easy:
const TodoItem = ([item, itemState]) => ...;
const TodoList = items => items.map(item => (<TodoItem item={item} />));
export default connect(state => _.zip(state.items, state.itemsState))(TodoList);
But updates to state can be painful, because items
and itemsState
must be kept in sync:
- When removing an item, corresponding itemState must be removed.
- When reordering items, itemsState must be reordered too.
- When the list of todo items is updated from the server, it is necessary to keep ids in the UI state and do some reconciliation.
Is there any other option? Or is there any library that helps keeping the app state and UI state in sync?