How to handle cross-cutting concerns in redux reducers and actions - redux

Given a use case like the one in this question:
Best way to update related state fields with split reducers?
What is the best practice for dealing with actions in reducers that depend on state outside of their own state? The author of the question above ended up just passing the entire state tree as a third argument to every reducer. This seems heavy-handed and risky. The Redux FAQ lists the following potential solutions:
If a reducer needs to know data from another slice of state, the state tree shape may need to be reorganized so that a single reducer is handling more of the data.
You may need to write some custom functions for handling some of these actions. This may require replacing combineReducers with your own top-level reducer function.
You can also use a utility such as reduce-reducers to run combineReducers to handle most actions, but also run a more specialized reducer for specific actions that cross state slices.
Async action creators such as redux-thunk have access to the entire state through getState(). An action creator can retrieve additional data from the state and put it in an action, so that each reducer has enough information to update its own state slice.
In my use case, I have an action "continue" that determines what page a user is allowed to go to in a multiple-form / multi-step process, and since this depends on pretty much the entire app state, I can't handle it in any of my child reducers. For now, I've pulled the store into the action creator. I use the current state of the store to calculate an action object that fires to my "page" reducer, which changes the active page. I will probably install redux-thunk and use getState() in this action creator, but I'm not committed to this approach yet.
I guess this isn't too bad of a solution since there is only one action (so far) that must be handled this way. I'm just wondering if there is a better solution, or if there is a way to re-structure my state and reducers to make it easier, or if what I'm doing is within best practices for Redux. If there are any similar examples out there, that would be helpful also.
To give some more context, my state tree currently looks like this:
{
order: order.result,
items: order.entities.items,
activePage: {
id: 'fulfillment'
// page info
},
pagesById: { // all the possible pages
fulfillment: {
id: 'fulfillment'
// page info
}
}
}
The active page is the page / section in which the user must enter data in order to proceed to the next page). Determining the active page almost always depends on the items state and sometimes depends on order state. The end result is an app where the user fills out a few forms in succession, hitting continue once the form is valid. On continue the app determines the next page needed and displays it, and so on.
EDIT: We've tried the approach of implementing a "global" reducer in combination with child reducers.
The implementation is like this...
const global = (currentState = initialState, action) => {
switch (action.type) {
default:
return currentState
}
}
const subReducers = combineReducers({
order,
meta
})
export default function (currentState = initialState, action) {
var nextState = global(currentState, action)
return subReducers(nextState, action)
}
The global reducer is first run on the whole app state, then the result of that is fed to the child reducers. I like the fact that I'm no longer putting a bunch of logic in action creators just to read different parts of state.
I believe this is in alignment with the principles of redux since every action still hits every reducer, and the order in which reducers are called is always the same. Any thoughts on this implementation?
EDIT: We are now using router libraries to handle the page state, so activePage and pagesById are gone.

If state.activePage depends of state.order and state.items, you may subscribe to the store and in case of modifications on "order" or "items" then dispatch a "checkPage" action which can set another active page if necessary. One way should to connect on a "top component" order and items, listen their values and change active page/redirect
Not easy to understand your concern, I hope my message will help. Good luck

Related

NgRx reducer function with condition

I have a side effect that detects the browser language and dispatches a browserLanguageSupported action if it is a language that my application can handle.
Now I have following reducer function that only updates the states preferredLanguage property in case it is not defined already. This is important because there are other actions that update this state property and I do not want a late browserLanguageSupported action to overwrite such a state update.
export interface State {
preferredLanguage: AppLanguage | undefined;
rehydrationComplete: boolean;
}
export const initialState: State = {
preferredLanguage: undefined,
rehydrationComplete: false
};
export const reducer = createReducer(
initialState,
on(LanguageActions.browserLanguageSupported, (state, {browserLanguage}) => {
if (!state.preferredLanguage) {
return {...state, preferredLanguage: browserLanguage};
}
return state;
})
);
Now for my question: Is it good practice to have such a condition in a reducer operator? The function itself is still pure. But I am not sure if it is good design or if I should solve it differently, lets say by adding state slice selection in the side effect that dispatches this action.
Btw. the reason I am not setting it directly in the initial state is because I get the browser language from an angular service and I am not sure if it is even possible to set initial feature state from service injection?
Best regards,
Pascal
I would to this the same way, so you get a đź‘Ť from me.
Adding a slice of the state into the effect just adds needless complexity.
The reducer contains the state, and it's OK to add logic to see if state needs to be updated or not.
Also, let's say you need to add this logic into another action/effect.
Having it in the reducer makes it easier to reuse if it's needed. Otherwise you end up with duplicate logic.
As long as the rejection (or mutation) of the data is irrelevant to the chain of actions & effects, this is absolutely valid.
However, it's worth noting that if the action in question triggers an effect which triggers an action, the triggered action will not know whether the data was rejected (or mutated) without checking the state—which is exactly what this pattern is attempting to avoid.
So, if you wanted to be able react to that rejection (or mutation), you would want to handle this in the effect. But, if you would proceed in exactly the same manner regardless of the result, then it belongs reducer.

Accessing state of one reducer from another - Redux

I have two reducers combined using combineReducers()
UI Reducer has a 'isLoginOpen' key (true/false) - When true a login modal appears.
Auth Reducer has 'isAuthenticated' key - (true/false) Which states if the user is logged in
I also have an action creator called OpenLoginModal() which causes the UI reducer to make 'isLoginOpen' to become true.
I only want to allow this behavior when 'isAuthenticated' is false. I.E, I only allow to show the login modal when the user isn't logged in.
The problem is: 'isAuthenticated' is in a different reducer, and I don't want to duplicate it to UI reducer.
How should I resolve this issue?
Reducers should be concerned about updating the state.
You should use selectors to calculate the state of the application including which actions are allowed.
In your case the code calling the action creator (or the action creator itself if using thunk) can select both values from the store and decide what to do.
Per the Redux FAQ entry on sharing state between reducers:
Many users later want to try to share data between two reducers, but find that combineReducers does not allow them to do so. There are several approaches that can be used:
If a reducer needs to know data from another slice of state, the state tree shape may need to be reorganized so that a single reducer is handling more of the data.
You may need to write some custom functions for handling some of these actions. This may require replacing combineReducers with your own top-level reducer function. You can also use a utility such as reduce-reducers to run combineReducers to handle most actions, but also run a more specialized reducer for specific actions that cross state slices.
Async action creators such as redux-thunk have access to the entire state through getState(). An action creator can retrieve additional data from the state and put it in an action, so that each reducer has enough information to update its own state slice.
Given your use case, I'd suggest writing a thunk action creator that checks state.auth.isAuthenticated, and only dispatches the action if that's false.

checks inside action creators

I am creating simple app, which makes GET requests to the server, then prepares recieved data and creates chart. There are few questions:
Where should I place code responsible for checking and preparing raw data. Currently I have it in my action creators, but maybe it needs to be in the component itself?
I need to check and compare prepared data with the data which is already used for the chart, and do not call re-render if it's the same or not valid. Where should I put this check? For now I think to place it inside action creators too. But for that I need to use getState() for accessing the state, doesn't look right.
Action creators seems right place for all these checks for me, because if data is not valid, I can simply not update my state with it, (e.g. do not dispatch certain action creator) Or maybe I have to update state with new data despite it is not valid?
given these action creators, what is the best place for described checks?:
export function fetchPopulations(term = "") {
return function (dispatch) {
dispatch(fetchingPopulations())
term=toTitleCase(term)
return fetch(`${API_URL}${term.replace(/\s/g, '%20')}`)
.then(response => response.json())
.then(json => dispatch(requestPopulations(json)))
}
}
export function requestPopulations(data = []) {
return {
type: REQUEST_POPULATIONS,
payload: data,
}
}
export function fetchingPopulations() {
return {
type: FETCHING_POPULATIONS
}
}
I would say you are doing it right.
In your example, requestPopulations and fetchingPopulations are the real action creators and fetchPopulations is a composing function (yes, composing functions for the win!).
Where should I place code responsible for checking and preparing raw
data. Currently I have it in my action creators, but maybe it needs
to be in the component itself?
Components are not the place for putting the business logic of our application. Components should only represent the View in our MVC. No API calls, no business logic, only props and state.
I need to check and compare prepared data with the data which is
already used for the chart, and do not call re-render if it's the same
or not valid. Where should I put this check? For now I think to place
it inside action creators too. But for that I need to use getState()
for accessing the state, doesn't look right.
Create modular functions (it really shines with code maintenance and reuse) for performing these checks, compose them together in another one along with your real action creators, and you can dispatch only if needed. Further optimization can be done inside component life cycle hook shouldComponentUpdate(nextProps, nextState). Also I think it is definitely not an anti-pattern to use methods with a signature like this:
export function myComposingFunction(params) {
return (dispatch, getState) => {
// ...
So you can use getState().
Action creators seems right place for all these checks for me, because
if data is not valid, I can simply not update my state with it, (e.g.
do not dispatch certain action creator) Or maybe I have to update
state with new data despite it is not valid?
No, do not update the state with useless data. If you do that you will re-render the entire tree for nothing. You were absolutely right to say "if data is not valid, I can simply not update my state with it, (e.g. do not dispatch certain action creator)"

How to have multiple reducers trigger updates based on a common set of actions without repeating yourself?

I would like many different redux actions in my app to all trigger common functionality in a specific reducer. I would like to avoid having to either repeat some flag in every action creator (like doThing: true) that the reducer looks for. I also don't want to have to have the reducer just look for every individual action that falls into this category, since that also requires someone to remember to do this every time they add a new action, like adding the flag.
I was thinking of dispatching a second action every time one of these actions is going to be dispatched. This would not be hard to do, but I'd rather not have 2 actions dispatched every time one thing happens. It seems like it would pollute the state history.
Is there a common way of solving this problem?
For more context to my specific problem, the specific feature is related to the API client my app uses to talk to our API. On every successful response, we'd like to do something in a reducer to update the state, and on every failed response, we'd like to do something else.
There are many different success and failure actions (such as ITEM_FETCH_SUCCESS or WIDGET_UPDATE_FAILURE), and adding a flag to all of them would be hard to remember to do when new ones are added.
Since all api requests go through a single function, that function COULD dispatch generic REQUEST_SUCCESS and REQUEST_FAILURE actions. But this would mean every response from the server would dispatch 2 actions (REQUEST_SUCCESS and ITEM_FETCH_SUCCESS). This is obviously not ideal since it would mean many more actions in my state history.
Assuming the generic REQUEST_SUCCESS and REQUEST_FAILURE actions are updating their own specific portions of the state-tree then it is fine to dispatch them as distinct actions. Doing this does not necessarily imply the pollution of your state history but can simply be a better description of the app's intentions.
ITEM_FETCH_SUCCESS: Change state for item
REQUEST_SUCCESS: Change state for request
WIDGET_UPDATE_FAILURE: Change state for widget
REQUEST_FAILURE: Change state for request
You can see that whilst the actions are intimately related, they are not necessarily the same thing as they change different parts of the state tree.
Accepting this, the question is: How best to implement the action-pairs so that adding new actions does not mean remembering to add its corresponding REQUEST_* partner?
I would consider applying a simple redux middleware component. This could intercept the return from your api and dispatch the appropriate REQUEST_* action automatically.
Here is an example from some live code. This middleware intercepts a disconnect event raised by a websocket and automatically dispatches a custom action as a result. It at least shows the principle:
//Dispatch a disconnect action when the websocket disconnects
//This is the custom action provided by the middleware
import io from 'socket.io-client'
import { actions } from './action'
const websocket = ({ websocketUrl }) => store => {
const socket = io(websocketUrl)
socket.on('disconnect', () => store.dispatch(actions.disconnect()))
}
export default websocket
//Apply the custom middleware via the redux createStore function
//Also include the thunk middleware because it is useful
import { applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import websocket from './middleware'
function websocketize (opts) {
return createStore => (reducers, initial, enhancer) => {
const middleware = applyMiddleware(thunk, websocket(opts))
return createStore(reducers, initial, middleware)
}
}
export default websocketize
// Create the top-level redux store passing in the custom middleware enhancer
const opts = {websocketUrl: env.WEBSOCKET_URL}
const store = createStore(reducers, websocketize(opts))
This implementation keeps everything inside your reducers as opposed to having logic outside in an interception(middleware). Both ways are valid.
Try a sub-reducer pattern. I usually feel gross when I see it used(because it is usually used wrong), but your situation sounds perfect.
Extract duplicate functionality out of your reducers to one single
sub-reducer.
Then pass that reducer as a function to all others that need it.
Then pass the action and state onto the sub-reducer.
The sub-reducer does it's thing and returns that slice of state to
your parent reducer to allow you to do whatever you want with it
there (ie return it, mutate some more, some logic).
Also if you are tired of worrying about typing out "all the stuff" for async then I highly recommend you try out redux-crud.js
It also is possible and a simple way to do that would be to give every action to one reducer and let it do that common mutation, in a single case:
case actionOne
actionTwo
actionThree
actionFour: {
//do common stuff here
}
. But you said it is not duplicated, it is similar, which means your case becomes complicated by branching logic. I also don't recommend this. Keep cases simple so you can easily catch invalid mutations. This should be a super power of redux that it is easy to catch mutation errors. And for this reason and many others I don't recommend normalizing data in the front end.

How to clear State in the #ngrx?

I am building an mobile app using Ngrx and Angular2. I would like to clear the Store when user logout from my application? Can anyone know how to do this?
You should have an clear action in each of your reducer, which will clean respective properties in the store. Dispatch clear actions in each of the reducer manually when you call logout. I am not sure right now if its there an option to clean the entire store in one go.
Alternative:
A more cleaner and faster approach would be. Whenever you call an action via store.dispatch it calls all your reducers with that action.type. Say your action type name is CLEAR, put this action in each of your reducer to clear the respective property of the store. And call store.dispatch with action.type = 'CLEAR' ONCE, it will clear all the properties of the store.
If it confuses let me know, I will try to explain with code.
The solution is to write the root reducer.
It's similar to this:
export function reducer(state: any, action: any): ActionReducer<any> {
if (action.type === 'CLEAR STATE') {
state = undefined;
}
return appReducer(state, action);
}
Check this: How to reset the state of a Redux store?
My guess as to a way to do this would be to have a component that has the store registered on it. Put it in a router-outlet or a structural directive that could force a destroy and init on the component when the value changes.
Since the store is registered as a provider for the component (unless I'm mistaken) and should be tied to it, it should be destroyed when the component is destroyed and a new one created with a new component. Just a thought. I haven't had time to test it.
Don't register the provider in a parent component though. I believe the injectables work through a hierarchy.

Resources