Accessing state of one reducer from another - Redux - 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.

Related

Naming convention for Redux actions to differentiate requests and model updates

Let's say my Redux global state looks as follows:
{
micEnabled : Boolean,
filterEnabled : Boolean
}
A React component has a "Enable Mic" button that, upon clicked, should perform an async operation (that may take a while) and resolves with a Promise. In order to run such an async operation I can add a custom Redux middleware into the store, or can use redux-thunk, etc. That's not the question.
Here my question: Which one should be the name of the Redux action invoked on "Enable Mic" click?
When there is no async stuff involved it's common to name Redux actions as "setters" (SET_CURRENT_TIME) or expressive actions (TOGGLE_FILTER) that will be directly used by reducers to update state. So one may suggest ENABLE_MIC for my use case above, but the fact is that such an action (let's say "action 1") should not directly update state.micEnabled.
Instead, my Redux middleware will intercept action 1 (ENABLE_MIC), run the async operation and, once resolved, dispatch yet another Redux action ("action 2") so the corresponding reducer would update state.micEnabled. So "action 2" could be MIC_ENABLED.
To summarize:
Click on button dispatches ENABLE_MIC.
Redux middleware intercepts it and performs async operation.
On resolved, middleware dispatches MIC_ENABLED.
Reducer updates state.micEnabled.
Ok, this makes lot of sense. The problem is that, within my actions, I also have tons of "common actions" that are dispatched to reducers to update state (such as TOGGLE_FILTER):
Click on checkbox dispatches TOGGLE_FILTER.
Reducer updates state.filterEnabled.
So both ENABLE_MIC and TOGGLE_FILTER represent "commands" or "requests", but just one of them (TOGGLE_FILTER) is used by reducers to update state. In the other side, the reducer also listens for MIC_ENABLED action (which is not a "command" or "request" but something that has happened or an event).
So, is there any recommendation for naming these kinds of Redux actions in a comprehensible so, by looking at the name of all my Redux actions, I can easily know which ones update state and which ones just dispatch another actions?
Treat it like an AJAX request and call them ENABLE_MIC_REQUEST which can result in ENABLE_MIC_SUCCESS or ENABLE_MIC_FAILURE (if this is possible in your scenario). You could cover everything in one action creator, using thunk, named something like enableMic. That should be fairly transparent.
Only ENABLE_MIC_SUCCESS would then flip micEnabled in the reducer. I'd recommend to rename that to isMicEnabled btw to make it super clear that it's a boolean flag.
When handling ENABLE_MIC_FAILURE you can show error messages, or do whatever is appropriate in your app.

Listen for state changes from redux-thunk action creator

I can't see a clear way to know when a particular action has fired or particular state has updated from the context of a redux-thunk action creator.
I want to do something like this:
Dispatch an action
Detect a possible recoverable error condition
Error condition dispatches a different action signalling recovery process initiating
Wait for recovery to complete
Proceed with current action or re-dispatch it
Concrete example:
User interaction triggers API call action
Note that API call failed, needs login
Dispatch 'LOGIN_REQUIRED' action, which pops up a <dialog> for user.
Wait for logged in state to change (or LOGIN_SUCCESS action to occur, whatever).
Make same API call again
If you want to check for a specific action to be dispatched, you'll need middleware.
If you want to, in effect, "subscribe a given bit of state", Redux doesn't provide a built-in way to do that. There are, however, a number of utilities that implement that kind of logic for you. See the Redux FAQ at http://redux.js.org/docs/FAQ.html#store-setup-subscriptions , and also the list of store subscription addons in my Redux addons catalog. (The list of Redux middlewares may also have something useful for the "listen for an action" scenario.)
for visualizing what happens in store you can use:
import {composeWithDevTools} from 'redux-devtools-extension';
and create your store as: (for instance with thunk middleware)
const store = `createStore(rootReducer,composeWithDevTools(applyMiddleware(thunk)));`
then you can use the extention in browser.
you can have access to your state by {connect} from 'react-redux' as props.
your actions then will change the state via reducers, this will cause your component receive new props so you can have your logic in
componentWillReceiveProps(nextProps) {/*your business logic*/}

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

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

How to trigger a single initial action in many redux reducers at once?

I am refactoring my app to use redux, and it's great.
One thing I'd like to do is to dispatch an initial action at the beginning, and every reducer would manage to initialize themselves at that moment.
Let's say i have a main.js that create the stores, the routes, etc. In that file, I could do:
store.dispatch({ type: 'app/init' });
If I do this, the action type app/init can be intercepted in each reducer which needs to initialize itself.
An example use case (among others)
When the app is launched, a third party library must be called to see if a user is currently authenticated. If so, a LOGIN_SUCCESS action must be triggered with that user data.
I'd like to see this code in the same file as the authentication reducer, triggered by a global init action (which would mean the store is created).
The problem
In the reducer (where init action is managed), other actions cannot be dispatched.
The advised way of implementing actions is by defining action creators, which is indeed very clean, and let us use middleware like thunks to dispatch other actions.
I could use init() action creators for each reducer (I define related actions and reducer in the same "ducks" file), but that means importing/calling each of them in main.js, which is what I was trying to avoid by dispatching the action directly.
How to get the best of all worlds by having one single app/init action dispatched, and being able to intercept it in each store and dispatch other actions?
Note: I thougth of just implementing those initialization code in each reducer, inline, but I do not have standard access to the dispatcher that way?
The reducer just calculates the next state based on an action. In Redux, it’s not the right place to put side effects like calling an API.
If you want to orchestrate an asynchronous workflow of actions, I suggest you to look at Redux Saga. It lets you define long-running functions (“sagas”) that can “wait” for specific actions, execute some side effects, and dispatch more actions.

All reducers will be invoked when an action is dispatched?

I am using combineReducers to combine all the reducers to create the store, does it mean that any action dispatched from any view will trigger all the reducers being invoked to check the action type?Is it kind of low efficiency?
Or I don't fully understand the redux design principle?
Yes, that is correct.
However one option you have to optimize this behaviour (suggested from the Redux docs) is to use 'reselect' https://github.com/rackt/reselect
Reselect basically allows you to create memoized selectors, whereby you can say that props A depends on state B and state C, and therefore only recompute props A if state B or state C changes.
Notice that this will still trigger all of the reducers to run (and go through the switch statement to see if the action might apply to them) - I believe there is no way around this behaviour. Using reselect however means that your top level component will only receive a prop/state change if there was an actual change that affects that state, rather than triggering a change every time and making React re-render everything, even when the change had no effect because it was somewhere unrelated. (The readme in reselect explains better)
Yes - all the reducers will get called when you dispatch the action. You will get one nice side effect to it. Because every reducer returns default state if the action isn't found you get your initial state set up with a single action.
It could be beneficial, although I haven't tried it yet, to have single action affecting multiple reducers aka changing the state in two different part of the store.
When you add reselect to it as #luanped suggested you can get a lot of sick results!
My current stack is:
Redux for state, actions management
Reselect for data transformation layer
React for views
Adding reselect to the work flow and making it work alongside reducers was the best thing that happened to me last week.
You can ignore actions of specific reducer by using https://github.com/omnidan/redux-ignore
import { combineReducers } from 'redux';
// redux-ignore higher-order reducer
import { ignoreActions } from 'redux-ignore'
combineReducers({
counter: ignoreActions(counter, [INCREMENT_COUNTER])
});
Also read about performance on official site
https://redux.js.org/faq/performance/#wont-calling-all-my-reducers-for-each-action-be-slow

Resources