React-Redux complex (deep) state objects - reactjs

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.

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.

Related

How do I deal with app state in a multi-"page" application?

I'm having trouble envisioning application state.
For a multi-page application, should each page only load a chunk of the app state?
For example, let's say I have an app that manages my favorite things, books, movies, and games. Each one of those domains will have their own page to manage them. Is the idea that only portions of app state are loaded based on what's needed in the current context?
My app state would look something like this, conceptually.
{
currentUser: { id: 9, userName: 'JUtah' },
books: {},
movies: {},
games: {}
}
However, if I browsed to Books Management, the app state would look like this:
{
currentUser: { id: 9, userName: 'JUtah' },
books: {
1: { title: 'Kung Fu for Kittens', author: 'Dr. Meowrtin Kibble' }
},
movies: {},
games: {}
}
If I browsed to Movie Management, this:
{
currentUser: { id: 9, userName: 'JUtah' },
books: {}
},
movies: {
1: { title: 'John Wick', star: 'Keanu Reeves' }
},
games: {}
}
and so on.
Is this correct? I'm struggling to determine what app state holds at any given time.
First of all, React's local state and Redux's global state are different things.
Let's assume you don't use Redux for the moment. State management is up to you totally. But, try to construct your components as pure as possible and use the state where do you really need it. For example, think about a favorites app as you said. The decision is, do you want to show all the favorites categories in the same UI? If yes, then you need to keep all of them in one place, in the App. Then you will pass those state pieces your other components: Book, Movie, etc. Book get the book part of your state for example. They won't have any state, your App does. Here, App is the container component, others are presentational ones or dumb ones.
Is your data really big? Then you will think about other solutions like not fetching all of them (from an API endpoint or from your DB) but fetch part by part then update your state when the client wants more.
But, if you don't plan to show all of them in one place, then you can let your components have their state maybe. Once the user goes to Book component, maybe you fetch only the book data then set its state according to that. As you can see there are pros and cons, in the first method you are doing one fetch and distributing your data to your components, in the second method you are doing multiple fetches. So, think about which one suits you.
I can see you removed the Redux tag, but with Redux you will have one global state in the store. Again, in one point you are doing some fetch then update your state. Then, you will connect your components when they need any data from the state. But again, you can have container/presentational components here, too. One container connects to your store then pass the data to your components. Or, you can connect multiple components to your store. As you examine the examples, you will see best practices about those.
If you are new don't think too much :) Just follow the official documentation, read or watch some good tutorials and try to write your app. When you realize you need to extract some component do it, then think about if you need any state there or not?
So, once the question is very broad then you get an answer which is too broad, some text blocks :) You can't see so many answers like that, because here we share our specific problems. Again, don't bloat yourself with so many thoughts. As you code, you will understand it better.

Redux-way in the real life

I want to ask the community about an ideological problem.
Lets imagine todo-list on react/redux, you have single state where todoItems array is served. But now lets imagine I want to have few components on the page that are render todoItems with different UI. And I need to update each these components on CRUD of todoItems. What is your architectural approach of this issue? Don't forget we have a large database and we can get todoItems with pagination only.
Update:
Lets make it clear. When we implement redux life cycle with this UI we have 2 options:
1) Serve one array of todoItems into singleton redux state object.
Advantages: all our components will updates by object changing.
Problems: we can't get ALL data from our database, but have to show different paginated/filtered data, so we can't implement pagination/filtering on frontend-side. We have a few different components and the have to render different objects collection. So it doesn't fit.
2) We can use different keys into our global redux state.
Advantages: we can independently get data for each component
Problems: other components will not feel when object changing in one of them. In this case we have to write custom code.
I just want to know maybe I'm missing something and we have other option or maybe someone have good architectural approach to this problem.
I bet your complications come from the point of view which unfortunately quite common among redux community: trying to keep redux shape as close to UI shape as possible.
Try no to think about redux state as a substitute for the Component states. What redux should know about is actual todos only (id, title, date of creation, etc.). Let Component-specific data like pagination stuff live in Components state. When user goes to next page in one of the Components what should be updated is this Component state (pageNumber, from, to, amount, etc.). redux should be updated only in case necessary todos are missing.
The useful analogy is to thinking about your redux as good old SQL-database: redux store state is data itself, selectors and actions are queries and stored procedures, React Components are views with selected data.
Update: Ok, seems like what you are looking for is state normalization. Separate todos details from the lists of ids. This way updates of todo fields will be sensed by all the Components. On the other hand you'll be able to keep separate collections of todos in different Components. Namely make state look like this:
{
funnyTodos: [ 'id1', 'id2' ],
boringTodos: [ 'id3', 'id4' ],
recentlyDoneTodos: [ 'id1' ],
todos: {
id1: { name: .... },
id2: { name: .... },
id3: { name: .... },
id4: { name: .... },
}
}
Implementing pagination in this case is just a matter of getting list of todos ids for the next page from back-end and then loading missing todos for given ids.

How to avoid potentially expensive re-calculating of computed properties with redux

Say I have animals in a redux store. It's a normalized object, the key is the id and the value is the object itself. Each animal has a key is_dog.
animals: {
'id1': { location: 'foo', is_dog: true, name: 'Dog1' },
'id2': { location: 'foo', is_dog: false, name: 'Cat1' },
'id3': { location: 'foo', is_dog: false, name: 'Cat2' },
'id4': { location: 'foo', is_dog: true, name: 'Dog2' },
}
I have a DogsList connected react component that looks at the store state and passes in an array of dogIds to the UI component. Calculation of dogIds is something like:
dogIds = _.chain(state.animals).filter('is_dog').map('id').value();
In the example above this would return ['id1', 'id4'].
However, let's assume I have thousands of animals, and each animal has other properties that change frequently, say currentLocation, lastFed, etc. I know that dogIds will only change if is_dog changes for any animal, but I'm not quite sure how to not do the filter and map calculations unnecessarily on every change to any animal. I tried using reselect but I don't think it really helps in this case.
I'm leaning towards storing dogIds in redux proper, and manually updating it only when relevant. Like when a new dog is added to animals, or if a cat changes to a dog (my analogy is falling apart here). But I feel like I'm doing something wrong by keeping computed properties in a redux store.
Perhaps you could look into using React's lifecycle methods like shouldComponentUpdate - here you could assess whether newDogIDs !== oldDogIDs and then only do stuff if they are different.

What is the use of immutable.js while using redux?

This is something I'am not getting right.
While using redux, in reducers we use the spread operator.
For e.g.
{...state,data : action.payload,fetching:false}
That is a new state object is created, rather than mutating the correct state right? (Please correct me if i'am wrong)
In such cases what is the use of immutableJS ??
It performs the same action as mentioned above right??
You are correct, the example you have shown is creating a new object and not mutating the state. It is fine for many cases, so if you don't feel that ImmutableJS is going to add anything for you, don't use it.
ImmutableJS was more useful before the spread operator was in common use in ES6 (I believe it is technically still only a proposal). If you are not using ES6, then the alternative is to use Object.assign which can get very messy, very quickly, especially with more nested structures.
ImmutableJS is still useful if you need to modify a single node deep within the state tree, but if this is the case, you can generally get around it by structuring the data in a different way.
When you have a simple flat state, you can easily manage it without extra libraries.
But let's consider something more complex, like the following
{
users: {
123: {
name: 'John',
lastName: 'Doe'
},
345: {
name: 'Bob',
lastName: 'Jack'
}
....
}
}
If you want to update a name for some user, it will be not so trivial
return {
...state,
users: {
...state.users,
[action.userId]: {
...state.users[action.userId],
name: action.newName
}
}
Pretty much code, isn't it? At this moment, you may want to look for another solution and immutable.js may help you do the same with one line:
state.setIn(['users', action.userId, 'name'], action.newName)
Making your state immutable ensures you that the state will not get modified outside of flux-flow. In very complex structures with a lot of levels and props being passed around it, it prevents the state from being mutated accidentally.

Redux store - Accessing keys of an object when not using arrays

I have a redux store like so:
{
'1f458697-e2c0-4d11-ada0-ee7b113c2429': {
name: 'Jim',
title: 'Developer',
active: true
},
'4498eb08-3786-495a-a66c-21a65138ab24': {
name: 'Darren',
title: 'Human Resources',
active: false
},
'c7975551-153f-4eab-a875-ed5445f76252': {
name: 'Francesa',
title: 'Chief of Internal Operations',
active: false
}
}
And I have a selector that gets an active user.
export const getActiveUser = (users) => _.find(users, { 'active': true });
My question is, whats the nicest way to include the ID of the user in the payload to use within my React component:
Is this is a bad practice and a sign that I should be using an array instead.
Should I just change my user payload to include the ID. This feels wrong as I would be duplicating data.
Should I manipulate that specific selector to include the users ID.
Yes, use arrays for table-like data and filter function for filtering the result set (if multiple users can be active at a time). If only one user can be active at a time, consider changing the db schema and create a separate variable activeUserId instead of having all of them contain unnecessary payload (and change the shape of the redux store accordingly).
- Is this is a bad practice and a sign that I should be using an array
instead.
Normalizing the data, so you can edit or delete items (users in your case) in O(1) is a good approach, which is also covered in the Dan Abramov's idiomatic redux course.
- Should I just change my user payload to include the ID. This feels wrong as I would be duplicating data.
Since you don't usually change IDs, adding them inside the user's object should not be a problem, and it should give you quick access to the ID. However, if you don't won't to duplicate that's acceptable as well.
- Should I manipulate that specific selector to include the users ID.
Part of the the selectors role is to create derived data, just like what you do when you filter the users. So if you want to the the ID to each user, this is a good place to do so. Don't mutate the objects, just create new user objects with ID. However, if you've got lots of users, you should use memoized selectors or add the ID as part of the payload to prevent performance issues.

Resources