Access other slice of state in reducer using #ngrx/store - redux

Given the following (and assuming we cannot change the state's structure):
StoreModule.forRoot({
a: aReducer,
b: {
b1: b1Reducer,
b2: b2Reducer
}
});
and b1Reducer is dependent on the value of a (for example because it contains something like user info).
What is the most idiomatic way to access (read-only) a in b1Reducer?
The solution I came up with is using #ngrx/effects, dispatch another action with a that can be used in the reducer:
#Effect()
augmentAction$ = this.action$
.ofType(Actions.Action1)
.withLatestFrom(this.store$)
.switchMap(([action, state]:[Action, AppState]) => {
const a = state.a;
return [new Actions.Action2(a)];
});
This works, but it becomes hard to manage if almost every action needs to be redispatched if a is used in many reducers. Is there a better way to handle this?

Related

Updating normalised data without causing more re-renders outside of the state slice that has been updated

I have some normalised data (items) within my redux store:
{
items: {
index: ['a','b'],
dict: {
a: {
title: "red",
},
b: {
title: "car",
}
}
},
...
}
So, if I want to update anything within an item object, the reducer looks like this:
...
const itemsReducer = (state = initialState.items, action) => {
switch (action.type) {
case itemsActions.types.UPDATE_ITEM: {
return {
...state,
[action.payload.itemId]: {
title: action.payload.title,
}
}
}
default: return state;
}
};
But this technique creates a new object for items, which can cause unnecessary components to re-render, when really it should only cause components that subscribe to state changes of the individual object to re-render.
Is there any way to get around this?
That is how immutable updates are required to work - you must create copies of every level of nesting that needs to be updated.
In general, components should extract the smallest amount of data that they need from the store, to help minimize the chance of unnecessary re-renders. For example, most of the time a component probably shouldn't be reading the entire state.items slice.
FWIW, it looks like you're hand-writing your reducer logic. You should be using our official Redux Toolkit package to write your Redux logic in general. RTK also specifically has a createEntityAdapter API that will do most typical normalized state updates for you, so you don't have to write reducer logic by hand.
I'll also note that the recently released Reselect 4.1 version has new options you can use for customizing memoized selectors as well.

Redux: is it an anti-pattern to put much logic and share date between slice reducers in thunks?

In the Redux Style Guide, it is strongly recommended to Put as Much Logic as Possible in Reducers:
Wherever possible, try to put as much of the logic for calculating a
new state into the appropriate reducer, rather than in the code that
prepares and dispatches the action (like a click handler).
What I'm not sure of is, if thunks are also considered to be "the code" of some sort. Besides, we've also been (mis?)using thunks to grab data from other slices of state.
Hypothetically simplified code snippet of such thunk:
const addX = x => (dispatch, getState) => {
const { data, view } = getState();
const { y } = view; // <-- here accessing data from `view` state.
const yy = doSomeLogicWith(y);
const z = doSomeMoreLogicWith(yy);
dispatch({ type: 'data/xAdded', payload: { x, z } });
};
Is this actually considered to be an anti-pattern in Redux? If so, what are the cons of doing this?
Yes, a thunk would qualify as "the code that dispatches the action" for this case. So, what the rule is recommending here is that if possible, the action would just contain y, and the function calls to doSomeLogicWith(y) and doSomeMoreLogicWith(yy) would ideally exist within the reducer instead.
Having said that, it's totally fine for a thunk to extract pieces of data from the state and include that in the action, and it's not wrong for a thunk to do some pre-processing of data before dispatching the action.
The style guide rule is just saying that, given a choice between running a particular piece of logic in a reducer or outside a reducer, prefer to do it in the reducer if at all possible.

Can I pass always the full state to reducers?

Is there any inconvenient at all if I design my reducers to, instead of reading only the partial state, had access to the full state tree?
So instead of writing this:
function reducer(state = {}, action) {
return {
a: doSomethingWithA(state.a, action),
b: processB(state.b, action),
c: c(state.c, action)
}
}
I destructure state inside doSomethingWithA, c or processB reducers, separately:
function reducer(state = {}, action) {
return {
a: doSomethingWithA(state, action), // calc next state based on a
b: processB(state, action), // calc next state based on b
c: c(state, action) // calc next state based on a, b and c
}
}
Would I'd be using more RAM? Is there any performance inconvenient? I understand that in javascript, a reference is always passed as parameter, that's why we should return a new object if we want to update the state or use Immutable.JS to enforce immutability, so... again, would it be of any inconvenient at all?
No, there's nothing wrong with that. Part of the reason for writing update logic as individual functions instead of separate Flux "stores" is that it gives you explicit control over chains of dependencies. If the logic for updating state.b depends on having state.a updated first, you can do that.
You may want to read through the Structuring Reducers section in the Redux docs, particularly the Beyond combineReducers topic. It discusses other various reducer structures besides the typical combineReducers approach. I also give some examples of this kind of structure in my blog post Practical Redux, Part 7: Form Change Handling, Data Editing, and Feature Reducers.

Is it possible to create a selector that accesses multiple branches of the state tree?

I am working on a Redux project with a bunch of reducers that are combined
const rootReducer = combineReducers({
reducer1,
reducer2,
...
});
The reducers each update a branch of the store
sampleStore = {
reducer1: {a1: 7, b1: 5, c1: 6, ...},
reducer2: {a2: 3, b2: 2, c2: 9, ...},
reducer3: {a3: 1, b3: 4, c3: 2, ...},
...
}
Now I am trying to add logic that requires access to both branches of the store without restructuring the entire project. For instance, I want a reducer with the ability to perform: set c3 = f(a1, a2).
At the moment I am trying to use reselect but I am getting lost. In reducer1/selector.js I have
export const derivedVariableSelector = createSelector(
a1Selector,
a2Selector,
(a1, a2) => a1 + a2
);
And I am trying to create a reducer
function setC3(state) {
return Object.assign({}, state, {
c3: state.c3 + derivedVariableSelector(state)
});
}
However, when setC3 is called, derivedVariableSelector will never receive enough of the store to do its job. Either it will receive the branch containing a1 or the branch containing a2, but I don't know of a way to supply both. Is this possible?
The short answer is "No", so long as you're using combineReducers to combine all your reducers.
The longer answer is taken from the redux documentation:
The combineReducers utility included with Redux is very useful, but is
deliberately limited to handle a single common use case: updating a
state tree that is a plain Javascript object, by delegating the work
of updating each slice of state to a specific slice reducer. It does
not handle other use cases, such as a state tree made up of
Immutable.js Maps, trying to pass other portions of the state tree as
an additional argument to a slice reducer, or performing "ordering" of
slice reducer calls. It also does not care how a given slice reducer
does its work.
The common question, then, is "How can I use combineReducers to handle
these other use cases?". The answer to that is simply: "you don't -
you probably need to use something else". Once you go past the core
use case for combineReducers, it's time to use more "custom" reducer
logic, whether it be specific logic for a one-off use case, or a
reusable function that could be widely shared.
On the same page of the docs are some suggestions for how the create reducers that share more of the state but it's going to be a bit more manual than comibineReducers is.
I haven't tried anything like this myself before, but following examples in the docs, something like this might work for you:
import { combineReducers } from 'redux'
import reduceReducers from 'reduce-reducers'
const reducer1 = (state = {}, action) => {
switch (action.type) {
// handle actions
default:
return state
}
}
const reducer2 = (state = {}, action) => {
switch (action.type) {
// handle actions
default:
return state
}
}
const reducer3 = (state) => {
return {
...state,
// use the root state for the reducerC value
reducerC: makeValue(state.reducer1, state.reducer2)
}
}
const rootReducer = reduceReducers(combineReducers({reducer1, reducer2}), reducer3)
This uses the library reduce-reducers to combine reducers in a different way.

Idiomatic Redux: Dispatch more than one action

I have a Redux application that shows a list of posts. The state is more or less this:
{
posts: [
{id: 1, title: 'My Post'},
{id: 2, title: 'Also this one'},
{id: 3, title: 'Other Post'}
],
visible_post_ids: [1, 2]
}
Whenever I load some posts I add them to posts, then I replace the content of visible_post_ids.
This is my action creator for loading posts:
function loadPosts (filters) {
return function (dispatch, getState) {
return fetch(`/posts.json?filters=${filters}`)
.then((response) => response.json())
.then((posts) => {
dispatch(postsLoaded(posts)) // Will update `posts`
const postIds = posts.map((post) => post.id)
dispatch(updateVisiblePosts(postIds)) // Will update `visible_post_ids`
})
}
}
My question is: is it idiomatic to dispatch two (or more) events from a thunk? Or should I dispatch only one and handle it in various reducers?
Quick answer : there is no problem to dispatch two or more actions from a thunk, I think it's a good practice,especially if API Call response contains answers to two completely different concerns.
I think it depends what you are trying to represent, in your case you can have one action that represent an add of new posts and two different reducers can catch it and do different tasks with it.
But you can see that as two different actions (your example) and it's great too.
As Sergey L said, in your case with a unique action (for your case) it can create an interesting "dependency"
If you don't consider scenario when it is possible to postsLoaded without calling updateVisiblePosts, it is better to handle the state change just in postsLoaded.
Especially if you need them to be in sync. For example, if you need a grantee that visible_post_ids does not contains Ids from not existing/loaded posts. Besides it minimizes the updates as each dispatch will cause processing in React.
On the other hand, having these actions separate can make code more clear as you have very simple implementation for each action.

Resources