I'm learning Redux, and am running into a problem related to ensuring a branch (Branch A) of my state tree has completed its update before I update another branch (Branch B). Branch B is dependent on the ID that is assigned to the newly updated item in Branch A, so I want Branch B's update function to dispatch after the dispatched function that updates Branch A is complete. I have multiple reducers that correspond to the various branches, and I am using combineReducers to combine them.
I use the following to dispatch the action updating Branch A from a component:
/* update Branch A */
this.props.dispatch(actions.addCriterion(this.state));
Sequentially dispatching the action that updates Branch B on the next line of code results in Branch B not seeing the updated state information caused by Branch A's dispatch.
/* update Branch B */
this.props.dispatch(actions.completeScoreGrid(this.props.alternatives, this.props.criteria));
However, everything works as desired if I use a setTimeout and wait a bit after A is dispatched before calling B. In this case the state update is complete and B see's everything it should
/* update Branch B after a 1 second pause */
var that = this;
setTimeout(function() {
that.props.dispatch(actions.completeScoreGrid(that.props.alternatives,
that.props.criteria));
}, 1000);
However, I'm sure using a set timeout is not the proper way of doing this. I've looked as redux-thunk, redux-promise, redux-promise-middleware, and redux-saga and I'm not sure which of these is the proper tool to use for this problem or whether any of them are the proper tool. What is the correct way to solve this kind of problem?
The problem here is that the criteria prop inside the component from which you dispatched these actions hasn't had a chance to update. However, the value of criteria inside the state atom itself has already updated. Allow me to explain this further.
Let's call this line of code Line 1:
this.props.dispatch(actions.addCriterion(this.state));
And let's call this line of code Line 2:
this.props.dispatch(actions.completeScoreGrid(this.props.alternatives, this.props.criteria));
After Line 1 has completed, the criteria in the redux state atom includes the new criterion. However, the criteria prop in Line 2 has not been refreshed yet, since React hasn't yet updated the component in response to the redux state change.
So how do you fix this? There are a few ways, but here's my best suggestion. Instead of passing in this.props.criteria to your completeScoreGrid action creator, you can use redux-thunk to enable async actions, and get the criteria from the redux state atom inside the async action creator. Here's how that async action creator might look, assuming the criteria are stored at state.criteria.
function completeScoreGrid(alternatives) {
return (dispatch, getState) => {
const state = getState();
const criteria = state.criteria;
dispatch({
type: 'COMPLETE_SCORE_GRID',
payload: {
criteria,
alternatives
}
});
}
}
You can then update Branch B in response to this action. Let me know if you have any questions.
I ended up using thunk-redux to solve the problem and marking Brandon Lewis's post above as the answer.
Also, there is another technique posted by Reddit user cyex in my Reddit post asking this same question. I tested it, and it also did what I wanted to accomplish. I've linked to the post below as well as pasted his solution here.
Link to Reddit post on this question
Can you post what your reducers look like? It doesn't sound right that the new state wouldn't be reflected in the second call.
But also when you say that Branch B is dependent on the ID that is assigned to the new item in Branch A... are you generating this ID in your reducer or in your action creator?
e.g. What if your code looked like this instead?
/* update Branch A */
var newCriterion = actions.addCriterion(this.state); // ID is assigned here, not in reducer
this.props.dispatch(newCriterion);
/* update Branch B */
var scoreGrid = actions.completeScoreGrid(this.props.alternatives, this.props.criteria, newCriterion.id);
this.props.dispatch(scoreGrid);
So the B reducer doesn't need to look at anything on another branch.
Related
Problem
Actions in my redux store are appearing to log-jam behind one another. I'm iterating through a set of thunks, which each call a number of actions to show they've started, succeeded, etc. When this happens, an action appears for a second in redux dev tools, then is erased.
If I post another action, then all the actions appear all at once, like container ships following the ever-given.
Link to gif of the issue
In this gif I connect to a testing database, afterwards, a number of operations dispatch. I can see those operations in the console, but not devTools. Then, I post another action via the onscreen button, and all the actions flow through at once.
I'm hunting for instances of mutated state, but all reducers destructure state into a new object via:
let newState = {...state}
Any tips?
EDIT:
When I dispatch the same operation from behind a button element, it works just fine. The code that's log jamming is being called by an event listener attached to an event emitter... maybe this has something to do with it?
After debugging, I've traced the problem back to the redux replaceReducer method. I call it 3 times in this sequence. The first and second invocation works fine, but on the third - the store stops receiving actions.
store.injectReducer = (key, asyncReducer) => {
storeTools.dispatchAction({type:"STORE_INJECT_REDUCER_" + key})
store.asyncReducers[key] = asyncReducer;
let combinedReducers = createReducer(store.asyncReducers);
storeTools.dispatchAction({type:"STORE_INJECT_REDUCER_" + key})
store.replaceReducer(combinedReducers);
storeTools.dispatchAction({type:"RESET"})
console.log("replaceReducer")
}
^^^
This code prints actions on the first 2 invocations, but on the third, it prints the first two actions, but not the third.
This bug was caused by invoking "replaceReducer" multiple times within the same thread. From what I now understand - if you call replaceReducer in a forEach loop... you're gunna have a bad time.
My solution was to create a function that stages multiple reducers - then calls replaceReducer once.
May folks from the future benefit from this knowledge.
My team and I are busy designing a redux store, including all the possible actions that can be fired. Due to the nature of our application, we have some inherent coupling/dependencies between different nodes (branches?) of our redux store. Our idea of tackling these dependencies in the most scalable manner was to extract it to its own separate node in the state tree. Let me give a simplified example to illustrate the structure we have in mind, and get to the problem that we're facing:
Say our state tree has the following:
SectionA: with data A as a bool
SectionB: with data B as an int
DependencySection: with a dependency that if A is true then B has to be 10 or larger
You're probably thinking, why do it this way? Why not just integrate the dependency into the reducer for SectionB? The reason is that these dependencies vary per client, and we're reading them from a database. It can link any part of the state tree to any other part and have its own actions that need to happen.
Now, my question is, how do we reduce actions while taking into account these dependencies? If our application flow is:
User causes TOGGLE_DATA_A action
SectionA reducer updates data A
DependencySection updates data B based on the dependency
What happens if we introduce a SectionC, with data C that is dependent on data B? It seems like a new action needs to be fired when the DependencySection updates data B, with the action being that data B has been updated. This would mean firing an action while another action is being reduced, which is obviously not allowed. Alternatively, it seems like making the different reducer sections execute in a VERY specific order would also solve the issue, but this is surely an anti-pattern.
The only solution we can come up with is to have middleware that repeatedly fires UPDATE actions after every action until the state no longer changes. For example, after the TOGGLE_DATA_A action updates data B (via the dependency on data A), the next UPDATE action would update data C (via the dependency on data B), and the next UPDATE would update nothing, stopping the loop. This is quite hacky.
So, is there a better way to deal with reducing such a dependent state? Or should we be structuring our state tree differently?
One of the key concepts behind Redux is that reducer logic is just functions, and if you need to order your state update handling in a specific order, you can do that yourself by writing explicit code for that. So no, "making different reducer sections execute in a specific order" is not an "anti-pattern", it's absolutely a valid and encouraged approach with Redux.
There's examples and discussions of this approach in the Structuring Reducers - Beyond combineReducers section of the Redux docs, and in my blog posts Idiomatic Redux: The Tao of Redux, Part 1 - Implementation and Intent and Practical Redux, Part 7: Feature Reducers. I'll paste in a basic hypothetical example:
export function commentsReducer(state = initialState, action, hasPostReallyBeenAdded) {}
// elsewhere
export default function rootReducer(state = initialState, action) {
const postState = postsReducer(state.post, action);
const {hasPostReallyBeenAdded} = postState;
const commentState = commentsReducer(state.comments, action, hasPostReallyBeenAdded);
return { post : postState, comments : commentState };
}
Project (Todolist) was created with immutable library, source here
Store structure: project have many tasks, In redux store: State - map, projects, tasks - Records
When I asyncly remove project ...
export const removeProject = project => (dispatch) => {
if (!isProjectExist(project)) return Promise.resolve()
return projectService
.delete(project)
.then(
() => {
dispatch(remove(project))
console.log("post removeProject resolved")
},
handleError,
)
}
.... that was created after initialization - it will be deleted and properly unmounted, but when project was passed as initialState - ProjectList will not be rerendered, and ProjectItem try to render itself with stale data, and fail, as in picture
It have tests
It looks like reducer returs changed data, but I use immutablejs, and previously i use normalizr-immutable, but I thought that source of issue in this library and write my own normalizeInitialState (source), it did not help, now I think that maybe source of problem in redux-immutable
I struggled entire day on solving of this problem
creator of redux says
I don't think this is something we can fix. React state changes are
asynchronous and React may (or may not) batch them. Therefore, the
moment you press “Remove”, the Redux store updates, and both Item and
App receive the new state. Even if the App state change results in
unmounting of Items, that will happen later than mapStateToProps is
called for Item.
Unless I'm mistaken, there is nothing we can do. You have two options:
Request all required state at App (or a lower, e.g. ItemList) level
and pass it down to “dumb” Items. Add safeguards to mapStateToProps
for “currently unmounting” state. For example, you may return null
from render in this case. Potentially we could have the component
generated by connect() return null from its render if mapStateToProps
returned null. Does this make any sense? Is this too surprising?
Hm, I never saw stubs like return (<div></div>) or safeguards in mapStateToProps in others code
markerikson
I'm not entirely sure I follow what exactly your problem is, but as a
guess: it sounds like the child component is re-rendering before the
parent is. This is a known issue with React-Redux v4 and earlier. The
v5 beta fixes that issue. Try installing react-redux#next and see if
that takes care of your problem.
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.
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