Best practices for redux mapStateToProps? - redux

As far as I can tell, reducers change the state of the tree, mapStateToProps transform the state tree for the UI. However, the boundary is not clear. For instance, consider the example from the "Computing Derived Data" docs (http://redux.js.org/docs/recipes/ComputingDerivedData.html). See below.
My first instinct is to put the calculation of the visible todos in the reducer (and not mapStateToProps), that is, whenever a todo or the visibility filter changes, update the list of completed or active todos. This has several advantages:
No need for Reselect
Having all the logic in one place helps reduce the learning curve (when onboarding) and probably also makes it easier to test (since the integration tests for mapStateToProps are simpler, if non-existent).
On the other hand, 2) is subjective. So guidance on mapStateToProps would be helpful.
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}
const mapStateToProps = (state) => ({
todos: getVisibleTodos(state.todos, state.visibilityFilter)
})
const mapDispatchToProps = (dispatch) => ({
onTodoClick: (id) => dispatch(toggleTodo(id))
})
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
Update in response to #DDS:
To update multiple interrelated states based on one action means that these states can become out of sync... This could mean the visibleTodoList acquires items that don't exist in the original.
If by multiple interrelated states you mean visibilityFilter and todos, then as per the redux docs, one idiomatic solution is to refactor the state tree so that they are one state. There are other approaches mentioned in the docs as well. Certainly, as you allude, you now have the burden of ensuring the code to compute the derived state (visible todos) is always called. Once the code base gets large, a custom combineReducer (another idiomatic solution) that does additional transforms before returning the state makes sense.
Note that the code lives in separate files and the execution order of reducers is not guaranteed. Also, reducers don't normally have access to sibling state meaning they cannot derive data from it
See my comments above.
The example above may be contrived but the point is that to make the data stable, it's best to have a single source every component can rely on, and this requires the data to be normalized.
Yea, it all comes down to normalized vs denormalized state. I'm not yet convinced that normalized state is always the way to go...for the same reason NoSQL databases are sometimes the way to go.
Reasoning about more complex state [normalized and denormalized state] becomes difficult very quickly. This is why it is better to not put derived data in state.
Ah I see your point. Six months from now I may not see that visibleTodos is derived state (and so should be treated as readonly). Unexpected things will result.

NOTE: The are my two cents based on my personal experience, and not necessarily in line with best practices.
Redux state
This should be normalised, primarily because it makes writes (inserts/updates/deletes) easy to reason about.
Normalising redux state would mean that you should not be storing derived data in the redux state.
Derived data:
My personal experience of using react/redux (before the nice docs on http://redux.js.org/docs/recipes/ComputingDerivedData.html emerged) made me try to follow something that you (OP) are striving for: simplifying the places where code is written.
After accepting the normalization principle, the natural place for me to start writing derivation or 'state view' logic was in the react component's render function (which now sounds a little ugly). As code evolves, the render function becomes neater by creating derivation functions and keeping them outside the react component.
This creates an easy mental model for people working with the codebase:
redux-state: Normalized store
mapStateToProps: Just maps state to prop values in a dumb way
ReactComponent: Contains all the logic to 'view' the right pieces of the state and then render it. Further modularisation done as deemed necessary by author.

The reason Reselect is used over putting this in a reducer and in the state is analogous to why React is used instead of jQuery.
To update multiple interrelated states based on one action means that these states can become out of sync. Maybe one reducer interprets ADD_ITEM to mean "upsert" when another, coded in a different file months later by someone else, takes it to mean "insert with duplicates allowed". This could mean the visibleTodoList acquires items that don't exist in the original.
Note that the code lives in separate files and the execution order of reducers is not guaranteed. Also, reducers don't normally have access to sibling state meaning they cannot derive data from it.
The example above may be contrived but the point is that to make the data stable, it's best to have a single source every component can rely on, and this requires the data to be normalized. Storing derived data means storing the same data in multiple places and forms, but being interdependent.
Having a single source and having data flow unidirectionally, prevents disagreement on which data is authoritative.
The state should be thought of as clean-room data. It has a number of properties that make it dependable and easy to reason about:
All state is immutable, which means it can be stored anywhere and treated like a value without fear it will later be modified by external code.
All state is serializable which guarantees that it contains no code or cycles and can be shipped and stored easily.
It is normalized so it contains only one copy of each datum. There can be no desynchronisation or disagreement between different parts of the state. This makes the state harder to make internally inconsistent.
Reasoning about more complex state becomes difficult very quickly. This is why it is better to not put derived data in state.
Note that what may seem like a great and simple idea when you code it may trip you up years later when that little side project has blossomed into a highly sought-after essential tool. It's not hard to hack up working code on the first round so doing things the Redux Way is very much a future-proofing strategy.

Related

Read RTK-Query state in a selector without having to pass the cache key

Very simple:
Let’s say we use RTK-Query to retrieve the current weather.
For that, I pass to the endpoint the arg ‘Paris’ as city.
It will serve the current weather of my « game ».
Then later, in a Redux selector, I need to compute some derived state based on that current weather.
How to read the state without having to pass the cache key « Paris »?
Indeed, that knowledge of « Paris » was only necessary at the beginning of the app.
It seems that with RTK-Query we’re stuck since you have to pass the argument that was used (the cache key) to the endpoint#select method.
Am I right in saying that RTK-Query does not currently allow that kind of state reading:
« select that current (and single) store entry X whatever the argument that was needed at loading time is ».
No, since that's an edge case.
Usually, there are multiple cache entries per endpoint, and there is also no concept of a "latest entry" or something, since multiple different components can render at the same time, displaying different entries for the same endpoint - the concept of a "latest" there would come down to pretty random React rendering order.
The most common solution would be to just safe "Paris" somewhere in global state to have it readily available, or to write your selector against RTKQ store internals by hand (although there might be changes to the state internals in the future).

createSlice: Redux store access differs between reducers and selectors?

when using createSlice, I can give the slice a name, eg: 'person'. Within reducer code, store refers obviously to the 'person' part of the whole redux store (provided that I used the proper configureStore setup). So I can say store.firstName = 'Bernhad'; That's fine.
in selectors, things seem to be sligthly different: store is bound to the (whole?) redux store. So I have to code 'firstNameSelector = (store) => store.person.firstName;'
I think this is quite asymetric - one time having to use store.person, the other time just store - and I wonder: WHY IS THIS SO?
Any explanation highly appreciated!
Bernhard
A slice is that: a "part" of your state, that works on it's own. It does not know anything exists outside of it. You could mount it at different positions in your store or even in other stores and it would still behave exactly the same.
Selectors are a different thing. With a selector you look from the outside perspective at the whole store and select out those values you need. Within that selector you might want to access multiple slices to derive a value, so you'd always look at "the whole".

Why reducer must return new state?

Why reducer must return new state what is the reason for that .Why can't we return the updated state? Is that the pattern that we must follow or what?Also please let me know that ngrx and redux are they completely different?
I think because the view layer needs to compare current state and previous state, they should be different objects. Also, it can support other features like debugging, time travel.
In both the library, They return a newly modified state or the original state
Just going through the official docs of both NgRX reducer and Redux reducer
NGRX Reducer
Reducers in NgRx are responsible for handling transitions from one state to the next state in your application.
Reducer functions are pure functions in that they produce the same output for a given input. They are without side effects and handle each state transition synchronously. Each reducer function takes the latest Action dispatched, the current state, and determines whether to return a newly modified state or the original state
Redux Reducer
Reducers specify how the application's state changes in response to actions sent to the store.
Regardless of the state management pattern, You need to change the state through reducers as actions are responsible fpr source of information for the store. They are the entry points to interact with store in Both NgRx and 'redux', moreover in Vuex too.
As per the state management library implementation, I guess they both follow same principle of Actions, Reducer to update the state async. There might be some possibly they may have different feature.
Hope this helps!
Both libraries aim to manage a state which is only manipulated in particular, predefined ways; reducers are the access they provide to the state.
By limiting the ability to manipulate the state directly, they make it easier to understand how a particular state was reached; it is always possible to reach a particular state by dispatching the same actions again, and a given state can only be reached as a result of the actions dispatched to state (at least, this is the ideal - impure* reducers would potentially lead to different states being reached from the same actions).
If we imagine a state manager which allowed functions to manipulate state, which is what would be required to return a mutated version of the original state, then it would be far more difficult to understand how a given state was reached, as the store could have been manipulated at any point by any function.
This article gives a good overview of the key ideas behind redux and explains why redux does the things it does. Here are the relevant parts for your question:
State is read-only
The only way to change the state is to emit an action, an object describing what happened.
This ensures that neither the views nor the network callbacks will ever write directly to the state. Instead, they express an intent to transform the state. Because all changes are centralized and happen one by one in a strict order, there are no subtle race conditions to watch out for. As actions are just plain objects, they can be logged, serialized, stored, and later replayed for debugging or testing purposes.
Changes are made with pure functions
To specify how the state tree is transformed by actions, you write pure reducers.
Reducers are just pure functions that take the previous state and an action, and return the next state. Remember to return new state objects, instead of mutating the previous state. You can start with a single reducer, and as your app grows, split it off into smaller reducers that manage specific parts of the state tree. Because reducers are just functions, you can control the order in which they are called, pass additional data, or even make reusable reducers for common tasks such as pagination.
I have far less experience with ngrx, though as it seems like a redux-inspired store, I'll presume it follows more or less the same principles. I'd be happy to be proven wrong, in which case I can update this answer.
*An impure function function would do one or many of the following:
Access state other than the arguments it was passed
Manipulate the arguments it was passed
Contain a side effect - something which affects state outside of itself
Mutating state is the most common cause of bugs in Redux applications, including components failing to re-render properly, and will also break time-travel debugging in the Redux DevTools. Actual mutation of state values should always be avoided, both inside reducers and in all other application code.
Use tools such as redux-immutable-state-invariant to catch mutations during development, and Immer to avoid accidental mutations in state updates.
Note: it is okay to modify copies of existing values - that is a normal part of writing immutable update logic. Also, if you are using the Immer library for immutable updates, writing "mutating" logic is acceptable because the real data isn't being mutated - Immer safely tracks changes and generates immutably-updated values internally.
From Redux Doc.

Ngrx complex state reducer

I struggle finding the right way to mutate my state in an ngrx application as the state is rather complex and depending on many factors. This Question is not about doing one piece of code correct but more about how to design such a software in general, what are doe's and don'ts when finding some hacky solutions and workarounds.
The app 'evolved' by time and i wan't to share this process in an abstracted way to make my point clear:
Stage 1
State contains Entities. Those represent nodes in a tree and are linked by ids. Modifying or adding an entity requires a check about the type of nodes the new/modified ones should be connected with. Also it might be that upon modifying a node, other nodes had to be updated.
The solution was: create functions that do the job. Call them right in the reducer so everything is always up to date and synchronus when used (there are services that might modify state).
Stage 2
A configuration is added to the state having an impact on the way the automatically modifyed nodes are modifyed/created. This configuration is saved in it's own state right under the root state.
The solution:
1) Modify the actions to also take the required data from the configuration.
2) Modify the places where the actions are created/dispatched (add some ugly
this.state.select(fromRoot.getX)
.first()
.(subscribe(element => {this.state.dispatch(new Action({...old_payload, newPayload: element}))})
wrapper around the dispatch-calls)
3) modify the functions doing the node-modification and
4) adding the argument-passing to the function calls inside the reducer
Stage 3
Now i'am asked to again add another configuration to the process, also retrived by the backend and also saved in another state right under the root state
State now looks like:
root
|__nodes
|__config_1
|__config_2
i was just about to repeat the steps as in stage 2 but the actions get really ig with all the data passed in and functions have to carry around a lot of data. This seems to be wrong, when i actually dispatch the action on the state containing all the needed info.
How can i handle this correct?
Some ideasi already had:
use Effects: they are able to get everything from state they need and can create everything - so i only need to dispatch an action with only the actions payload, the effect then can grab everything from the state it needs. I don't like this idea because it triggers asynchronus tasks to modify the state and add not-state-changing actions.
use a service: with a service holding state it would be much like with effects but without using actions to just create asynchronus calls which then dispatch the actions that relly change state.
do all the stuffi n the component: at the moment the components are kept pretty simple when it comes to changing state as i prefer the idea that actions carry as little data as possible, since reducers can access the state to get theyr data - but this is where the problem occus, this time i can't get hands on the data i need.

In Redux, why do you need a seperate reducer to manage the other reducers (as shown in documentation)

http://redux.js.org/docs/introduction/CoreConcepts.html
As shown in the bottom of the document, a separate reducer was created to manage the previously created reducers.
I am very new to redux and am not understanding the reason. Thanks
This is simply a design pattern in Redux. As noted further on in the docs here, Redux lets you do reducer composition, which lets different reducers handle different slices of the state. From the docs:
Note that each of these reducers is managing its own part of the global state. The state parameter is different for every reducer, and corresponds to the part of the state it manages.
As your application grows larger, it will make sense to logically split up reducers to make your application easier to comprehend, and combine them back together. Redux supplies combineReducers() to help you do this. In Redux's Todo app example, as found here, there is a reducer to handle todos, one to handle the visibility filter for todos, and a reducer that combines them with combineReducers().
Imagine you have a global state that looks like this
{
routing : some data,
restaurants : some_data ,
user : some_data,
neighborhood : some_data,
}
You can handle everything in one big reducer, or otherwise you handle each part in a different reducer.
You could have a look at an example here: https://github.com/maxgallo/deliveroo/blob/master/src/js/reducers/index.js

Resources