Given my initial redux state is :
const state = {
currentView: 'ROOMS_VIEW',
navbarLinks: List([
{name: 'Rooms', key: 'ROOMS_VIEW'},
{name: 'Dev', key: ''}
]),
roomListsSelected: {group: 0, item: 0},
roomLists: [
{
name: "Filters",
expanded: true,
listItems: [
{ icon: 'images/icon-warning.svg', name: 'Alerts', filter: room => room.hasAlert },
{ icon: 'images/icon-playlist.svg', name: 'In Progress', filter: room => room.progress > 20 },
{ icon: 'images/icon-playlist.svg', name: 'Almost Done', filter: room => room.progress > 90 },
{ icon: 'images/icon-playlist.svg', name: 'Complete', filter: room => room.status === 'complete' },
{ icon: 'images/icon-playlist.svg', name: 'Recently Completed', filter: room => false },
{ icon: 'images/icon-playlist.svg', name: 'All Rooms', filter: room => true }
]
}
],
rooms: List(generateRooms())
}
I need to make a reducer that does this:
state.roomList[n].expanded = !state.roomList[n].expanded
I am new to using a Redux workflow and the best way to solve this is to make roomList an immutable.js object or write some code to make a deep clone of my state object.
Also state.roomList will have new data pushed to it from future features.
Summery / Question: What is the best way to return a new state object in a reducer when making changes like this deep in the state, or should I change the structure of the Redux state object?
What I did In the end Immutable seems the way to go. There are some tricks with Immutable to reduce react rendering time and it meets all the project requirements. Also it is early enough in the project to use a new library without making major changes.
Both Redux and React's Context API deal with "prop drilling". That said, they both allow you to pass data without having to pass the props through multiple layers of components.
Immer is a library that simplifies the process of writing immutable update logic. Immer provides a function called produce , which accepts two arguments: your original state , and a callback function.
Any time any piece of the Redux state is updated, our Component will re-render, regardless of whether the store update was related to our component or not.
Immutability of redux state is necessary since it allows detecting redux state changes in an efficient manner. This implies that whenever we want to modify a redux state, we must create a new copy of it and do modifications to that copy - which then becomes the new redux state.
First, idiomatic Redux encourages you to "normalize" your state and flatten it as much as possible. Use objects keyed by item IDs to allow direct lookups of items, use arrays of IDs to denote ordering, and anywhere that one item needs to refer to another, it only stores the ID of the other item instead of the actual data. That allows you to do simpler lookups and updates of nested objects. See the Redux FAQ question on nested data.
Also, it looks like you're currently storing a number of functions directly in your Redux state. Technically that works, but it's definitely not idiomatic, and will break features like time-travel debugging, so it's heavily discouraged. The Redux FAQ gives some more info on why storing non-serializable values in your Redux state is a bad idea.
edit:
As a follow-up, I recently added a new section to the Redux docs, on the topic of "Structuring Reducers". In particular, this section includes chapters on "Normalizing State Shape" and "Updating Normalized Data", as well as "Immutable Update Patterns".
Reducer composition:
De-compose your reducers into smaller pieces so that a reducer is small enough to deal with simple data structure. eg. In your case you may have: roomListReducer
listItemsReducer
listItemReducer
. Then at each reducer, its going to make it much more easier for you to read which part of the state you are dealing with. It helps a lot because each of your reducer is dealing with small piece of data that you don't have to worry things like 'should i deep copy or shallow copy'.
Immutable
I personally don't use immutable.js
because I prefer to deal with plain objects. and there are just too much code to change to adopt a new API. But the idea is, make sure your state changes are always done through pure functions. Therefore, you can just simply write your own helper functions to do what you want, just make sure that they are tested thoroughly when dealing with complex objects.
Or simply enough, you can always deep copy your state in each reducer, and mutate in the copy then return the copy. But this is obviously not the best way.
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