ngrx : how the reducers function are invoked, when it is invoked? - javascript

I am trying my hands on ngrx library for managing the state of my application. I have gone through many ngrx documents and git pages. I understand that there are three important concept:
Store
Reducer and
Action
Store is the single source of data for our application. So any modification or retrieval of data is done through Actions. My question here is what exactly happens when an action is dispatched to the store? How does it know which reducers is to be invoked? Does it parses all the reducers registered to the store? There can be multiple actions with the same name in that case what happens?
Thanks in advance.

A picture is worth a thousand words...
Source: Building a Redux Application with Angular2
Example Code: ngrx-todo-app
Demo: Todo App using #ngrx/store and #ngrx/effects

My question here is what exactly happens when an action is dispatched to the store?
All of the registered reducers get a chance to handle the action
How does it know which reducers is to be invoked?
All of the registered reducers get invoked. Try putting console.logs into all the reducers and you can see for yourself.
Does it parses all the reducers registered to the store?
Yes
There can be multiple actions with the same name in that case what happens?
If you have multiple actions with the same name they would be treated the same. For example if I dispatched type "ADD" with payload 3 and then dispatched a different action called type "ADD" with payload 3 it would be the same thing.
Ngrx isn't that smart. Let's say we have the following reducers:
const reducers = {
blog: BlogReducer,
post: PostReducer,
comment: CommentReducer
}
Say I dispatch 'ADD_COMMENT'. Basically BlogReducer will first try to handle it, followed by PostReducer, and finally by CommentReducer. The ordering is determined by how you specified the reducers object above. So if I did this:
const reducers = {
comment: CommentReducer,
blog: BlogReducer,
post: PostReducer
}
CommentReducer would be the first one to try and handle the 'ADD_COMMENT'.

Related

How do i access state of one reducer in other in react redux. State flow between reducers and store

I'm an absolute beginner in react-redux and I watched many videos and articles,docs and couldn't understand how the state flows between reducers and between store to reducer.
I am having state for each reducer like this
const initState = {todos:[]}
cont reducer = (state=initState,action) ....
Reducer 2 similarly with different state
initState2 = {todo:" "}
reducer2 = (state=initState2,action) ...
And then I import and combine the reducers. Here I am using two different reducers which have different states and is it the right way of doing things ? If so, How can redux be called single state if each reducer have its own individual state.
Don't we have a single state in the store which is accessed by all the reducers and dispatch actions to change state of store directly instead of changing the reducer's store. Any help is appreciated and it may seems like a silly question but filling the gaps is really important and many beginners have same doubt as mine and please do help. Thank you
You only need 1 reducer to store your todos.
How can redux be called single state if each reducer have its own
individual state.
The application effectively has only 1 global store where all the application state is stored. What the reducer returns is what effectively gets stored in the store.
The configuration of what is stored is a map (key-value) where the key is defined in the root reducer, and the value is what is returned from the reducer function.
The way you have to look at it is that the view is "dumb", in that the only thing it does is tell the app what it wants by dispatching an action. This action is just an event that is marked with some string you give it to identify clearly what it is the view wants. The reducer intercepts this action and updates the state in the store accordingly. This state in turn is accessible to all the components in your app. So it clearly is global.
In your example, the view would just tell the application for example: "Add a todo". The reducer will intercept this message and return an array with the added todo. This returned array is what will be saved in the store.
If you want a seperate "todo", this will probably refer to the currently "active" to do. Marking it as such will make the purpose more expressive.
This is single state because your root reducer will end up with something like:
{
"activeTodo": activeTodoReducer
"todos": todosReducer
}
And you can access these key / values in your components throughout the entire application.
How can redux be called single state if each reducer have its own individual state.
Because the state is not saved in the reducers. The state is only saved in the store and there is only one store. That is why is called single state.
To create the store:
const store = createStore(myBeautifulReducers)
In your case, myBeautifulReducers would be:
const myBeautifulReducers = combineReducers({reducer, reducer2});
myBeautifulReducers will be an object that contains both reducers (reducer and reducer2) and the logic you wrote in each of them (switch statements and so on).

How to listen to redux action stream in component

I'm working on a React application that uses the following architecture:
redux
typesafe-actions
redux-observable
My question is: How can I execute an UI action on specific redux action?
For example, suppose we have the following async actions defined with typesafe-actions:
export const listTodo = createAsyncAction(
'TODO:LIST:REQUEST',
'TODO:LIST:SUCCESS',
'TODO:LIST:FAILURE',
)<void, Todo[], Error>();
An Epic will watch for listTodo.request() and send the API call, then convert the response to a listTodo.success() action. Then the redux reducer will be triggered by listTodo.success() action and store the todo list into redux store.
In this setting, suppose I want to do the following things in an component:
dispatch a listTodo.request() action to retrieve all the actions
After the async request is done (i.e. after listTodo.success() action appears in the action stream), redirect the UI to a second path
So my question is, how could I watch the action stream and react to the listTodo.success() action?
UPDATE: To avoid being too specific, we can think of another case. I want to simply display an alert with window.alert() after listTodo.success() appears in the action stream. Or simply console.log(), or whatever that changes local state (instead of global redux state). Is there a way to implement that?
UPDATE 2: There is a similar question here, but for Angular w/ ngrx. What I want to do is exactly the thing described in above post, but in React / redux-observable fashion:
import { Actions } from '#ngrx/effects';
#Component(...)
class SomeComponent implements OnDestroy {
constructor(updates$: Actions) {
updates$
.ofType(PostActions.SAVE_POST_SUCCESS)
.takeUntil(this.destroyed$)
.do(() => /* hooray, success, show notification alert ect..
.subscribe();
}
}
With redux the components update based on state.
If you want to update a component based on an action than you update the state in the reducer, such as setting {...state, success: true} in the reducer. From there you simply read the state into your component as you normally would and if the state is changing to success than you show your window.
Might be a little late but I solved a similar problem by creating a little npm module. It allows you to subscribe to and listen for redux actions and executes the provided callback function as soon as the state change is complete. Usage is as follows. In your componentWillMount or componentDidMount hook:
subscribeToWatcher(this,[
{
action:"SOME_ACTION",
callback:()=>{
console.log("Callback Working");
},
onStateChange:true
},
]);
Detailed documentation can be found at https://www.npmjs.com/package/redux-action-watcher
I feel like a dialogue should be a side effect, so you'd put them in epics

NgRx - Reducers priority and execution order against store

If different reducers are associated to the same action and are performing changes to the store, are they receiving the same version of it before any edit happens? Is there a priority to consider?
Example:
Lets assume you dispatch an action Update to edit an entity on server side, once data is successfully updated, an effect will dispatch an Update_Success action with an instance of the newly received version of entity as payload. A first classic and logical reducer will use it to update the store:
case ItemActionTypes.UPDATE_ITEM_SUCCESS: {
const item = action.payload;
return {
...adapter.upsertOne(item, state),
loaded: true,
loading: false
};
}
Now lets assume that in a different file, you have a different reducer associated to the same action and needs to compare the newly received entity against the old one in store:
case ItemActionTypes.UPDATE_ITEM_SUCCESS: {
const item = action.payload;
const oldItem = state.entities[item.id];
const changes = getAffectedProperties(item, oldItem);
// ...
}
The question is: Is there any chance oldItem is actually holding the newly received item instead of the old one as the first reducer may have already updated it? Is it a first come first serve case? or is there anything on its implementation that guarantees all reducers are performing changes to the the same version of the store and maybe merging those changes at a further step?
If different reducers are associated to the same action and are
performing changes to the store, are they receiving the same version
of it before any edit happens? Is there a priority to consider?
Reducers only have access to their piece of state. So you're not supposed to really care about that.
Also, when one action is dispatched, all the reducers are called in a synchronous way. Which means that if for the ACTION_A you're modifying your store from different places (/ different reducers), your selectors (or even the view), will only be refreshed once. When all the updates for that action are applied on the store.

Inject reducer for on demand component which was not in the store or combined reducers initially

I'm trying to build some modular SAP so many teams can work separatelly.
Basically, I want my containers to be independent in terms of container, store, reducers, sagas.
The actual question is (example code):
I render a basic template:
<div>
<a onClick={emitLoadUserListAction}>Load user list</a>
<UserList/>
</div>
At this point, I make use of 1 reducer for UserList to keep the array of users (empty at the beginning).
Let's assume I have a saga, waiting for this data to come as a user list in a json.
Store:
{
UserList: []
}
Once the saga fetches the data, publishes an action modifiying the current store:
Store:
{
UserList: [{name:"john",counter:0},{name:"pepe",counter:0}]
}
Now my UserList component can list this as we have the mapStateToProps pointing to this part of the store.
this.props.userList.map ( (userData,i) => { return <User data={userData}> } ))
So now everything is working like a charm if User component is just a normal component.
But what if User is actually a container, which is expecting to work on its own, with its own state I didn't connected yet via its own reducer. I don't want his parent to manage it. I want user to be independent as I could pass its location in the store with reselect selector or similar, or I could just pass the index in the array as a prop, so I could be the selector. This way I would have store injected in props, but I won't have reducer.
I'm pretty sure many of you already pass through this but I couldn't find a proper answer.
As you can see the idea is to have a component, which is loading on demand, not in the initial combineReducers, not handled by its parents, just render, and reducer injected to work on its own.
If I could have just a way to load its reducer on demand then, I would not store the data in the UserList but it will be a composition of reducers.
Thanks a lot in advance.
I'm continuing on from my comment and the question that followed so I can expand on it without the restrictions of the comments section.
Yes, my library calls replaceReducer on the store to in order to, well, replace the reducer with the new one included. In order to do so, I provide a Higher-Order Component (HOC) which bundles the component with it's associated reducer and performs the replacement when it is mounted.
The interface looks something like this:
export const MyBundledComponent = bundle(MyComponent, myReducer)
The only requirement for it to work is that the component is mounted within a Provider from react-redux. This gives the HOC access to the store on React's context the same way the connect HOC does. This isn't really a very prohibitive restriction though, as most redux apps have a Provider at the top of the tree already.
Hope this helps.
So far I found resources like this:
https://medium.com/#jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7
which allow you to inject reducers on demand by replacing the main reducer by using the Redux store API store.replaceReducer(nextReducer)
The problem with this solution is the need to have access to the main store object from the child component that should be encapsulated.
For the moment working not ideal solution that I found is to deliver the encapsulated component with a "multiple components reducers" meaning that the reducer assumes there could be more than one component under the same parent where each one has different ids.
So each action should check the payload ID, in order to get the state from the store object.
This would mean a small change in the hierarchy as the component would not be child but sibling.
Following the previous example, imagine that we list a shallow version of the user list and then you show more data once u click on any user:
`
Store: {
UserList: [], // basic info, id plus minimal data
users: {} --> userReducer // listing each user by key
}
`
This way the user component will expose multiUserReducer instead of logic for just one.
This obviously means the reducer is loaded in advance, even if you never load any user componet.

Why do you need 'Actions' as data in redux?

Redux documentations says I should make actions and action creators, like this:
function addTodo(filter) {
return {
type: SET_VISIBILITY_FILTER,
filter
}
}
Then write reducers, like this:
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
});
}
}
I then invoke the action using dispatch:
store.dispatch(addTodo("Ask question on stackoverflow"));
It seems there's a one-one correspondence between actions and reducers; the sole purpose of the action is to select a reducer and provide input data for that reducer.
Why don't we skip the middle man and identify actions with reducers and action creators with functions producing reducers? Then dispatch would take a single argument, a reducer/action of type State => State:
// Action/reducer. (Parametrised state transformer, really.)
const addTodo = text => state => {
return Object.assign({}, state, {
visibilityFilter: action.filter
});
}
// Dispatch takes as a argument the action/reducer
store.dispatch(addTodo("Ask question on stackoverflow"));
You'd lose the ability to serialise actions, but otherwise, it seems you'd get rid of boilerplate action creators and express more clearly the connection between actions and reducers. If you're in Typescript, you also get typechecking of the data in actions, which is difficult to express otherwise.
So what reasons for having actions as data am I missing?
The main purpose of action in Redux is to reduce the state.
Reduce method will be called on array of actions (thats why it called a reducer). Example:
import reducer from './reducer';
const actions = [
{type: 'INIT'},
{type: 'SOME_ACTION', params: {...}},
{type: 'RECEIVE_DATA', data: [...]},
{type: 'SOME_ANOTHER_ACTION', params: {...}},
{type: 'RECEIVE_DATA', data: [...]},
...
];
const finalState = actions.reduce(reducer, undefined);
Action creators is a function that can create actions. It is not necessary that action creator will create only one action.
Actually, if your reducer is able to receive functions instead of objects - your actions will be functions and it will do the main purpose, but you can loose some benefits of Redux functional abilities.
In that case the reducer will be implemented like this:
function reducer(state, action) {
return action(state);
}
The reasons why you may create actions as {type: 'ACTION_NAME'} format:
Redux DevTools expects this format.
You need to store sequence of actions.
Reducer makes state transformations on worker.
Everybody in Redux ecosystem use this format. It's a kind of convention.
Hot reloading abilities (your stored functions will not be reloaded).
You need to send actions as is on server.
Debugging benefits - to see the stack of actions with actions names.
Writing unit tests for reducer: assert.equal(finalState, expectedState).
More declarative code - action name and parameters are about "what to do" and not about "how to do" (but addTodo('Ask question') is declarative too).
Note about coupling between action creators and state changes
Just compare two notations:
First:
function someActionCreator() {
return {
type: "ADD_TODO",
text: "Ask question on stackoverflow"
}; // returns object
}
Second:
function someActionCreator() {
return addTodo("Ask question on stackoverflow"); // returns function
}
"In both cases we see that code is declarative and action creator is decoupled from state change. You can still reuse addTodo or dispatch two addTodo's or use middleware or dispatch compose(addTodo('One'), addTodo('Two')). The main difference is that we created Object and Function and place in code where state changes.
There is NOT a one-to-one mapping between actions and reducers. Per Dan Abramov's comments at https://github.com/Pitzcarraldo/reduxible/issues/8 :
It reinforces a very common misconception about Redux: namely that action creators and reducers are one-to-one mapping.
This is only true in trivial examples, but it is extremely limiting in real applications. Beginners exposed to this pattern couple reducers and action creators, and fail to realize they're meant to be many-to-many and decoupled.
Many reducers may handle one action. One reducer may handle many actions. Putting them together negates many benefits of how Flux and Redux application scale. This leads to code bloat and unnecessary coupling. You lose the flexibility of reacting to the same action from different places, and your action creators start to act like “setters”, coupled to a specific state shape, thus coupling the components to it as well.
As for actions and the "type" parameter, the other answers are right. That's deliberately how Redux was designed, and that was intended to give the benefits of serialization for debugging purposes.
Good question.
Separating actions from state changes is really a Flux pattern, rather than a specifically Redux thing. (Though I will answer the question with reference to Redux.) It's an example of loose coupling.
In a simple app, tight coupling between actions and state changes might be fine. But in a larger app, this could be a headache. For instance, your addTodo action might trigger changes in several parts of the state. Splitting actions from state changes - the latter performed in reducers - allows you to write smaller functions, which are easier to reason about and more testable.
Additionally, decoupling your actions and state changes allows your reducer logic to be more reusable. e.g. Action X might trigger state changes A and B, whilst action Y only triggers state change A.
Furthermore, this decoupling gives rise to a Redux feature called middleware. Middleware listens to action dispatches. It doesn't change the state of the app, but it can read the current state and the next state, as well as the action information. Middleware is useful for functionality extraneous from the core application logic, e.g. logging and tracking (dev tools were mentioned in a previous answer).
[UPDATE] But why have actions as objects?
If it were simply a matter of functions calling other functions, that decoupling would be much less explicit. Indeed it may even get lost; since most actions only enact one state change, developers might tire of a single function calling a single function and do away with the separation entirely.
The other thing is the Flux data flow model. One-way data flow is very important to the Flux/React paradigm. A typical Redux/React goes something like this: Store state -> Higher order React components -> Lower order React components -> DOM. The action is the aberration in the model; it's the backwards arrow transmitting data from the view to the store. It makes sense to make the action something as loud and emphatic as possible. Not a mere function call, but a dispatched object. It's as if your app were announcing Hey! Something important happened here!

Categories