Should actions like showing/hiding loading screens be handled by reducers to related actions or generated by action creators themselves? - redux

Given that you have some global view (showing a loading screen, for example) that you may want to happen in many cases, is it more appropriate to create an action creator/action pair for that behavior or to have the reducer for the related actions handle the transition?
This is hard to describe succinctly, to illustrate what I mean, here's a couple of examples. Which is better, and why?
A
function showLoading () {
return 'SHOW_LOADING';
}
function hideLoading () {
return 'HIDE_LOADING';
}
function fetchPostsRequest () {
return 'FETCH_POSTS_REQUEST';
}
function fetchPostsSuccess () {
return 'FETCH_POSTS_SUCCESS';
}
function doSomethingAsync () {
return dispatch => {
dispatch(showLoading());
dispatch(fetchPostsRequest());
// other logic
dispatch(hideLoading())
dispatch(fetchPostsSuccess());
}
}
function rootReducer (state = {}, action) {
const payload = action.payload;
switch(action) {
case 'SHOW_LOADING':
Object.assign({}, state, {isLoading: true})
break;
case 'HIDE_LOADING':
Object.assign({}, state, {isLoading: false})
break;
// other reducers for handling success/request stuff
}
}
B
function fetchPostsRequest () {
return 'FETCH_POSTS_REQUEST';
}
function fetchPostsSuccess () {
return 'FETCH_POSTS_SUCCESS';
}
function fetchPostsFailure () {
return 'FETCH_POSTS_FAILURE';
}
function doSomethingAsync () {
return dispatch => {
dispatch(fetchPostsRequest());
// good
dispatch(fetchPostsSuccess());
// bad
dispatch(fetchPostsFailure());
}
}
function rootReducer (state = {}, action) {
const payload = action.payload;
switch(action) {
case 'FETCH_POSTS_REQUEST':
Object.assign({}, state, {isLoading: true})
break;
case 'FETCH_POSTS_SUCCESS':
Object.assign({}, state, {isLoading: false /* other logic */})
break;
case 'FETCH_POSTS_FAILURE':
Object.assign({}, state, {isLoading: false /* other logic */})
break;
}
}
I prefer A because it seems more sensible to me to describe these behaviors in one place rather than duplicate the logic of state management, but I have heard a maxim in the redux community that actions should describe what happened or is happening, rather than be imperative commands. In which case, is this just a semantic issue where a term like "ASYNC_OPERATION_START" is better than "SHOW_LOADING"?

Think how this particular piece of code will evolve.
Use this to make decisions.
For example, you are likely to have more than one set of items that can be loading. You may eventually have two lists of items side by side, or one below the other. Thus you will want them to have separate isLoading state just like they have separate lists of IDs.
How would the code change in both options? It seems that having less actions is simpler because this lets you keep isLoading state of the particular list close to other information about it, and also not worry that you forgot to reset its state in the action creator. So in this case I'd choose option B.
On the other hand, if we're talking about a use case like showing a UI notification, I'd probably fire that as a separate action. It exists fairly independently of the server response that caused it: the notification needs to be hidden after a while, two notifications may live on the screen at the same time, etc. So, with this use case, option A seems a better fit.
In general, you should ask yourself:
How will this piece of code likely evolve?
Are these two actions really the same one or are they just related but independent?

Related

how to update a specific property in a state using ngrx

how do i update a specific property of object in a state?
i need to update the property offerPrice but still want to retain the other state values in the object
this is what i have in my effects
case OffersActionTypes.SET_TOTAL_OFFER_PRICE:
return {
...state, offerDetails: {
...state, singleOfferDetails: [
...state.offerPrice, action.payload
]
}}
but on the redux tools it giving me this
If you look carefully, you're not copying the parts of state that need to be copied. I believe you want something more like:
return {
...state,
offerDetails: {
...state.offerDetails,
singleOfferDetails: {
...state.offerDetails.singleOfferDetails,
offerPrice: action.payload
}
}
}
Of course, this can also be achieved with something like:
const newState = { ...state };
newState.offerDetails.singleOfferDetails.offerPrice = action.payload;
return newState;
I would argue, however, that your store is too embedded. Generally you want to keep things as flat as possible, although I'll grant you that it's not always a simple process. Best of luck!

Should I dispatch multiple actions in a single action creator, or change multiple properties in a single action type?

Say you are fetching data from a database - is it better to use an action creator like:
dispatch(fetchDataStart());
//then on success
dispatch(fetchDataSuccess(data));
And then have the reducer part look like:
case FETCH_DATA_START:
return { ...state, isFetching:true };
case FETCH_DATA_SUCCESS:
return { ...state, isFetching:false, data:action.data };
Or is it better to separate the fetching-logic from the data-logic and do something like this:
dispatch(fetchDataStart());
//then on success
dispatch(fetchDataFinish());
dispatch(updateData(data));
reducer:
case FETCH_DATA_START:
return { ...state, isFetching:true };
case FETCH_DATA_FINISH:
return { ...state, isFetching:false };
case UPDATE_DATA:
return { ...state, data:action.data };
The initial. The latter doesn't make much sense unless it's executed async.
Normally you would use an action to describe your intent fetchData is a reasonable name. A reducer will set the state to loading. Than the middleware will run with the the action and afterwards the middleware will trigger an action to inform the fetch is done, something like fetchSuccess and fetchError. You're allowed to do whatever is in the middle. If you want to trigger a FETCH_DATA_LOADING action after your fetchData action, because you have need for a very cool reducer to listen for it, please do so.The same argument for when the middleware returns. Please keep your actions as generic as possible so the middleware logic can be reused.

Redux will execute all subscription callbacks every time an action is dispatched?

Gee, I feel foolish about this, but I have read every part of: http://redux.js.org/ (done the egghead tutorials, and read 4 times the FAQ at: http://redux.js.org/docs/faq/ImmutableData.html
What I did was stub one of my reducers, to always return state, and that is the only reducer being called (checked with breakpoints). Even so, my subscribe event is being called every time the reducer returns state. What Do I not understand? (Action.SetServerStats is being called at a 1Hz rate, and the subscribe is also being called at a 1Hz Rate
BTW the Chrome Redux Extension says thats states are equal, and the React Extension for Chrome with Trace React Updates, is not showing any updates.
I will be glad to remove the question, when someone clues me in. But right now, what I see each each of the reducers being called at 1Hz, and all of them returning the slice of the store that they got (state).
So do I not understand subscribe, and that it returns every time even when the store tree does not get modified (and it is up to react-redux to do shallow compare to figure out what changed if any?)
create store & subscribe
let store = createStore(reducer, initialState, composeWithDevTools(applyMiddleware(thunk)))
store.subscribe(() => console.log("current store: ", JSON.stringify(store.getState(), null, 4)))
reducers.js
import A from './actionTypes'
import { combineReducers } from 'redux'
export const GLVersion = (state = '', action) => {
switch (action.type) {
case A.SetGLVersion:
return action.payload
default:
return state
}
}
export const ServerConfig = (state = {}, action) => {
switch (action.type) {
case A.SetServerConfig: {
let { ServerPort, UserID, PortNumber, WWWUrl, SourcePath, FMEPath } = action.payload
let p = { ServerPort, UserID, PortNumber, WWWUrl, SourcePath, FMEPath }
return p
}
default:
return state
}
}
export const ServerStats = (state = {}, action) => {
switch (action.type) {
case A.SetServerStats:
return state
// let { WatsonInstalled, WatsonRunning, FMERunning, JobsDirSize } = action.payload
// let s = { WatsonInstalled, WatsonRunning, FMERunning, JobsDirSize }
// return s
default:
return state
}
}
export default combineReducers({ GLVersion, ServerConfig, ServerStats })
Correct. Redux will execute all subscription callbacks every time an action is dispatched, even if the state is not updated in any way. It is up to the subscription callbacks to then do something meaningful, such as calling getState() and checking to see if some specific part of the state has changed.
React-Redux is an example of that. Each instance of a connected component class is a separate subscriber to the store. Every time an action is dispatched, all of the wrapper components generated by connect will first check to see if the root state value has changed, and if so, run the mapStateToProps functions they were given to see if the output of mapState has changed at all. If that mapState output changes, then the wrapper component will re-render your "real" component.
You might want to read my blog post Practical Redux, Part 6: Connected Lists, Forms, and Performance, which discusses several important aspects related to Redux performance. My new post Idiomatic Redux: The Tao of Redux, Part 1 - Implementation and Intent also goes into detail on how several parts of Redux actually work.

redux reducer vs action

Newbie question regarding reducer vs action. From redux documentation:
Actions describe the fact that something happened, but don’t specify
how the application’s state changes in response.
and
Given the same arguments, reducer should calculate the next state and
return it. No surprises. No side effects. No API calls. No mutations.
Just a calculation.
So if we consider the following scenario:
User can place points on a map and get route between those points.
When user first clicks on a map this is his starting point. When he clicks for the second time - this is his ending point. Consequent clicks add points between the previous point and end location.
After each point is added (except for the first one) route must be calculated between new point and previous point. So if i have S -> A -> F and add point B (S -> A -> B -> F) two routes must be calculated A -> B and B -> F
So we kind of have two side effects upon adding any 3+ point:
New point is placed not at the end of the list
New route must be calculated to the Finish point.
If i model my Point structure as this:
// typescript
interface Point {
coordinates;
routeTo?;
}
Am I correct to perform item position calculation and route retrieval in Actions, eg:
// pseudo code
export function addMapPoint(newPoint) {
return (dispatch, getState) => {
const {points} = getState();
const position = getInsertPosition(points, newPoint)
dispatch(addPoint(newPoint, position));
if (points.length >= 2) {
const previousPoint = getPreviousPoint(points, position);
calculateRoute(newPoint, previousPoint).then(route => {
dispatch(updateRoute(newPoint, route))
})
}
}
}
To me this somehow contradicts to the "but don’t specify how the application’s state changes" - because from action i'm specifying where to insert my new point.
I could calculate the insert position in reducer, but then how do i fetch route data for the Finish point?
What is the correct approach here?
Assuming we have a calculateRoute function that accepts two points, and returns a promise that resolved the route between them.
First, let's create a simple action creator so we know our points are stored correctly:
let addPoint = (point, index) => {
return {
type: 'ADD_POINT',
point: point,
index: index
}
}
Then, let's handle this action in the reducer:
let reducer = (state = { points: [] }, action) => {
switch (action.type) {
case 'ADD_POINT':
return Object.assign({}, state, {
points: [
...state.points.slide(0, action.index),
action.point,
...state.points.slide(action.index + 1)
]
});
default:
return state;
}
}
Now, after the users add a point, we create an action using addPoint and dispatch it, so far so good, but this is the easy stuff.
The structure I strive for is to have a routes list in my reducer too, so let's extend it to support that:
let reducer = (state = { points: [], routes: [] }, action) => {
switch (action.type) {
case 'ADD_POINT':
return Object.assign({}, state, {
points: [
...state.points.slide(0, action.index),
action.point,
...state.points.slide(action.index + 1)
]
});
case 'UPDATE_ROUTES':
return Object.assign({}, state, {
routes: action.routes
});
default:
return state;
}
}
And the action creator will be:
let updateRoutes = (routes) => {
return {
type: 'UPDATE_ROUTES',
routes: routes
}
}
Please notice we're overriding the entire routes collection. For now it's OK, but probably in a production system you would want to optimize it a little bit.
Now we actually need to write some logic. I will assume a convenient assumption that we have a calculateRoutes that gets a collection of points, and returns a promise that resolves a list of respective routes, each route will be an object, containing two points and the actual route. Having said that, our thunk will now look like this:
addPointAndUpdateRoutes = (point, index) => {
return (dispatch, getState) => {
// First, update the point list
dispatch(addPoint(point, index));
// Now, recalculate routes
calculateRoutes(getState().points)
.then(routes => dispatch(updateRoutes(routes));
};
};
Which is way nicer in my opinion.
Now, off course that assuming we have a magical calculateRoutes function is not a serious assumption, though it's not a super hard task to implement this function in an optimized manner (meaning actually send the server only routes that we did not calculate before, etc). BUT this is just logic and NOT the state of the application, thus, as long as you keep the "contract" defined by the store and reducers, you are free to implement it any way you'd like.
Hope this helps you.

Redux - Is there any way to access store tree in reducer?

In my case, I have a store like:
{
aa: {...},
bb: cc // the result of computing with aa
}
I need to update aa and bb at the same time, but bb need to get the latest computation of aa.
Here is some code(React.js):
onClick(e) {
const { dispatch, aa, bb } = this.props;
dispatch(updateAa());
dispatch(updateBb(aa)); // can not get the latest computation of aa, it is the last computation..
}
So, is this mean that I need to get aa in bb's reducer?
And How can I do it?
Hope for helps!, Thanks!
don't use combineReducers.
Example
replace this code
export const a = combineReducers({
app,
posts,
intl,
products,
pos,
cats,
});
with
export default (state = {}, action) => {
return {
app: app(state.app, action, state),
posts: posts(state.posts, action, state),
intl: intl(state.intl, action, state),
products: products(state.products, action, state),
pos: pos(state.pos, action, state),
cats: cats(state.cats, action, state),
};
};
reducer would be like
const reducer = (state = initialState, action, root) => {....}
There are several possibilities, but it's tough to say which is best, given the vagueness of the code.
Ideally, your store should be normalized, meaning that each piece of data is only available in one place. Then you would calculate derived data after reading the store, such as when you use the selector pattern described in the guide to map the state of the store to what you might consider a materialized view that will be sent to your components as props. In this workflow, aa and bb would each be produced by selector functions, rather than stored in that store itself.
You could leave the reducer that updates aa and bb outside of combineReducers, so that it sees the whole state, rather than the state scoped down to aa and bb.
You could factor out your calculation code into a helper that could be called by updateAa and updateBb, and pass enough info in each action to make the calculation.
You could calculate the update before dispatching, so that the action contains the right value.
As David L. Walsh said, probably you should structure your reducers in a more logical way.
BUT If you still think you need it, you can use a thunk Middleware.
(https://github.com/gaearon/redux-thunk)
Redux Thunk middleware allows you to write action creators that return a function instead of an action.
Redux Thunk offers you a way to read the current state of the Redux store. In addition to dispatch, it also passes getState as the second argument to the function you return from your thunk action creator.
export function action() {
return function(dispatch, getState){
const state = getState()
dispatch({
type: "ACTION_WITH_SOME_PART_OF_STATE,
some_part_of_state: state.some_part
})
}
}
Ask yourself whether you've structured your reducers correctly. If a and b are not independent of one another, why are they separate reducers? I would try to merge them into a single reducer.
Based on Sheikh Abdul Wahid's answer, I had to do the following modification to make it work with history and connected-react-router:
Notice the () after the connectRouter(history)
import { connectRouter } from 'connected-react-router'
const createRootReducer = (history) => {
return (state = {}, action) => {
return {
...reducers,
router: connectRouter(history)(),
...rest of reducers
}
}
}
If this is a common use case for you, you can try writing your own function to combine reducers according to your needs, as recommended by the official Redux documentation:
Sharing data between slice reducers
Similarly, if sliceReducerA happens to need some data from sliceReducerB's slice of state in order to handle a particular action, or sliceReducerB happens to need the entire state as an argument, combineReducers does not handle that itself. This could be resolved by writing a custom function that knows to pass the needed data as an additional argument in those specific cases, such as:
function combinedReducer(state, action) {
switch (action.type) {
case 'A_TYPICAL_ACTION': {
return {
a: sliceReducerA(state.a, action),
b: sliceReducerB(state.b, action)
}
}
case 'SOME_SPECIAL_ACTION': {
return {
// specifically pass state.b as an additional argument
a: sliceReducerA(state.a, action, state.b),
b: sliceReducerB(state.b, action)
}
}
case 'ANOTHER_SPECIAL_ACTION': {
return {
a: sliceReducerA(state.a, action),
// specifically pass the entire state as an additional argument
b: sliceReducerB(state.b, action, state)
}
}
default:
return state
}
}
I highly recommend you to read this documentation page, where there are also other suggestions to share data between reducers, even using combineReducers for simple actions and other custom reducers for the special cases.
I hope these options help!
You can access the other reducer's data in actions and dispatch that data as a param.
actions.js
const actionFn = (param1) => {
return (dispatch, stateFn) => {
const { param2 } = stateFn().other.reducer;
dispatch({
type: ACTION,
param1,
param2,
});
};
};
reducer.js
case ACTION:
return reducerFn(state, data);
const reducerFn = (state, { param1, param2 }) => {
return {
...state,
someState: {
...state.riverhealth,
setParam1: param1
setParam2: param2,
},
};
};
Hope it helps!
If some reducer needs some data from another reducer, a simple solution is to merge them into a single reducer.
In my case, I need some data from another reducer and it is very difficult to manage them so I ended up merging them both.

Resources