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:
Is there any other option? Or is there any library that helps keeping the app state and UI state in sync?
Collection / Item Reducer PatternThis pattern allows you to have multiple states and use a common reducer to update each state based on an additional parameter inside the action object.
Some users prefer to keep every single piece of data in Redux, to maintain a fully serializable and controlled version of their application at all times. Others prefer to keep non-critical or UI state, such as “is this dropdown currently open”, inside a component's internal state. Using local component state is fine.
Redux restricts updating the state to this method only. This strict way of updating the state ensures that the state can not be changed directly either by view or any network callback. The only way to update a state is by defining the action and then dispatching it. Remember that actions are plain JavaScript objects.
Another approach inspired by normalizr:
{ ids: [12,11], // registry and ordering data: { 11: {text: 'Chew bubblegum'}, 12: {text: 'Kick ass'} }, ui: { 11: { selected: true, expanded: false }, 12: { selected: false, expanded: false } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With