I'm loving redux, but I'm not sure if I'm creating actions in a consistent way.
I feel like action types for API calls are pretty straightforward, let's say my API is doing something:
type: DO_SOMETHING_REQUEST payload: nothing or data we want to send
type: DO_SOMETHING_SUCCEEDED payload: the data returned
type: DO_SOMETHING_FAILED payload: the error (if we care about our users)
That's all dandy. However when we talk about actions that are a bit more front-end related, like changing a state switch that controls showing a modal, I see two distinct approaches.
type: CAT_PICTURE_MODAL_SHOW
type: CAT_PICTURE_MODAL_HIDE
or
type: CAT_PICTURE_MODAL_SET_VISIBLITY payload: true / false
I tend to go for the first option rather than the second, and set the state switch explicitly. I feel it's more descriptive, even if you write a bit more code.
Is it only for boolean kind of data that this show/hide, on/off, left/right, cute/ugly action types are recommended? Or does it depend on how you think the data will evolve in the future? A modal is obviously not going to get an action CAT_PICTURE_MODAL_HIDE_BETTER, but you could easily imagine CAT_PICTURE_LIKE to have a (high) numerical value in the payload.
This is an interesting question, and I don't think there is a black/white answer. Personally, I'm always conscious about the amount of code I write: the more code you write, the more you will have to maintain later.
I only use the multi-action method for async requests, when I can't dispatch the start and result of the action at the same time. Otherwise, I personally feel it's more concise and straightforward to have single action creators.
I don't think this discussion is limited to redux, but goes broader to any kind of function in your code: if you're creating a counter that you want to increment / decrease: do you write to methods incrementCounter() and decreaseCounter() or just one changeCounterBy(amountToIncrease)? The answer on this question is probably an answer to your original question. Anyway, good luck!
Related
If so will this lead to a performance issue ?
Secondly
Is there a way to specifically call a reducer without invoking all 100 reducers ?
This is specifically answered in the Redux FAQ:
https://redux.js.org/faq/performance#wont-calling-all-my-reducers-for-each-action-be-slow
It's important to note that a Redux store really only has a single reducer function.
However, even if you happen to have many different reducer functions composed together, and even with deeply nested state, reducer speed is unlikely to be a problem. JavaScript engines are capable of running a very large number of function calls per second, and most of your reducers are probably just using a switch statement and returning the existing state by default in response to most actions.
Also, "call a reducer" is the wrong mental model here. Actions are like "events" that are being broadcast - you don't ever "call a reducer" directly:
https://redux.js.org/style-guide/style-guide#model-actions-as-events-not-setters
Why reducer must return new state what is the reason for that .Why can't we return the updated state? Is that the pattern that we must follow or what?Also please let me know that ngrx and redux are they completely different?
I think because the view layer needs to compare current state and previous state, they should be different objects. Also, it can support other features like debugging, time travel.
In both the library, They return a newly modified state or the original state
Just going through the official docs of both NgRX reducer and Redux reducer
NGRX Reducer
Reducers in NgRx are responsible for handling transitions from one state to the next state in your application.
Reducer functions are pure functions in that they produce the same output for a given input. They are without side effects and handle each state transition synchronously. Each reducer function takes the latest Action dispatched, the current state, and determines whether to return a newly modified state or the original state
Redux Reducer
Reducers specify how the application's state changes in response to actions sent to the store.
Regardless of the state management pattern, You need to change the state through reducers as actions are responsible fpr source of information for the store. They are the entry points to interact with store in Both NgRx and 'redux', moreover in Vuex too.
As per the state management library implementation, I guess they both follow same principle of Actions, Reducer to update the state async. There might be some possibly they may have different feature.
Hope this helps!
Both libraries aim to manage a state which is only manipulated in particular, predefined ways; reducers are the access they provide to the state.
By limiting the ability to manipulate the state directly, they make it easier to understand how a particular state was reached; it is always possible to reach a particular state by dispatching the same actions again, and a given state can only be reached as a result of the actions dispatched to state (at least, this is the ideal - impure* reducers would potentially lead to different states being reached from the same actions).
If we imagine a state manager which allowed functions to manipulate state, which is what would be required to return a mutated version of the original state, then it would be far more difficult to understand how a given state was reached, as the store could have been manipulated at any point by any function.
This article gives a good overview of the key ideas behind redux and explains why redux does the things it does. Here are the relevant parts for your question:
State is read-only
The only way to change the state is to emit an action, an object describing what happened.
This ensures that neither the views nor the network callbacks will ever write directly to the state. Instead, they express an intent to transform the state. Because all changes are centralized and happen one by one in a strict order, there are no subtle race conditions to watch out for. As actions are just plain objects, they can be logged, serialized, stored, and later replayed for debugging or testing purposes.
Changes are made with pure functions
To specify how the state tree is transformed by actions, you write pure reducers.
Reducers are just pure functions that take the previous state and an action, and return the next state. Remember to return new state objects, instead of mutating the previous state. You can start with a single reducer, and as your app grows, split it off into smaller reducers that manage specific parts of the state tree. Because reducers are just functions, you can control the order in which they are called, pass additional data, or even make reusable reducers for common tasks such as pagination.
I have far less experience with ngrx, though as it seems like a redux-inspired store, I'll presume it follows more or less the same principles. I'd be happy to be proven wrong, in which case I can update this answer.
*An impure function function would do one or many of the following:
Access state other than the arguments it was passed
Manipulate the arguments it was passed
Contain a side effect - something which affects state outside of itself
Mutating state is the most common cause of bugs in Redux applications, including components failing to re-render properly, and will also break time-travel debugging in the Redux DevTools. Actual mutation of state values should always be avoided, both inside reducers and in all other application code.
Use tools such as redux-immutable-state-invariant to catch mutations during development, and Immer to avoid accidental mutations in state updates.
Note: it is okay to modify copies of existing values - that is a normal part of writing immutable update logic. Also, if you are using the Immer library for immutable updates, writing "mutating" logic is acceptable because the real data isn't being mutated - Immer safely tracks changes and generates immutably-updated values internally.
From Redux Doc.
Proponents of ngrx claim (here for example) that you can and should keep all your application state in a single Store. That would suggest the #ngrx/Store can be used for caching because the contents of the cache is a type of application state.
A cache in a web-application is a thing that returns data when it has it, and encapsulates the requesting of the data from the server when it doesn’t. The Wikipedia article on caching calls it a cache hit when the data is available and a cache miss when it isn't.
From a functional programming perspective we can immediately see that reading data from a cache is functionally-impure - it has a side-effect which is that data may be requested from the server and retained in the cache. I don't know how to do this with ngrx which, for example, requires its Selectors to be functionally-pure.
It might help to consider this Caching With RxJs Observables in Angular tutorial (rxjs is said to be extremely complementary to ngrx). We don't have to scroll far to find the getFriends() function complete with side-effect:
getFriends() {
if(!this._friends){
this._friends = this._http.get('./components/rxjs-caching/friends.json')
.map((res:Response) => res.json().friends)
.publishReplay(1)
.refCount();
}
return this._friends;
}
Also the contents of the Store seem to be universally available to the entire application. The only controls are on how the state can be updated, but it is foolish to go poking around in a cache's raw data unmediated because there are no guarantees about which cache items are available and which are not available.
Hopefully these concerns can be relieved and there's a way of doing this that I've missed. Please can you show me a good way to go about using #ngrx/Store as a cache?
You can do something like this
friends$ = createEffect (() =>{
this.actions$.pipe(
.ofType(ListActions.LOAD)
.withLatestFrom(
this.store.select(fromSelectors.loadedList),
(action: fromList.Load, store: fromList.State) => store.friends
)
.switchMap((friends: Friend[]) => {
if (friends) {
return [new ListActions.Loaded(friends)];
}
return this.http
.get('/friendApi')
.map((friends: Friend[]) => new ListActions.Loaded(friends))
.catch(err => Observable.of(new ListActions.Failed()));
});
}
With ‘withLatestFrom’ operator, we get the store (loaded list) state into our effect with the dispatched action and check if the list is filled if so; dispatch the existing list otherwise make the rest call and update our store in the reducer.
For a detailed answer refer to this Medium post
In ngrx there are reducers (pure functions) which change the state (or cache) if you will. Those reducers get triggered by actions which you dispatch on the store.
From the store you request slices of data by selectors and subscribe to their changes.
To implement a cache logic you check if data is available and if not you
dispatch an action like "LoadDataSliceAction" which would trigger a side Effect which then loads the data into the store.
Does this help?
It’s worth noting that browsers come with a cache as standard, and it’s the real deal, encapsulating requests and encapsulating data, because it possesses none of the limitations of nrgx discussed in the question.
We’re using the browsers cache for storing entities these days. Borrowing my definition from here:
An entity is an object with long-lived data values that you read from the server (and possibly write back to it).
Entity data is only one kind of application data. Configuration data, user roles, the user’s prior selections, the current state of screen layouts… these are all important data that may benefit from coding with standard ngrx techniques, but they’re not entities.
This usurps one of Redux's fundamental principles (ngrx being an implementation of Redux), namely that the ngrx store is the single source of truth of your whole application. Here we are saying something different. Here we are saying that the browser cache is the single source of truth for our entities, and the ngrx Store is the single source of truth for everything else (actually it's slightly worse than that: if an entity is editable, then the Store has to assume responsibility for it while it is being edited).
The basics are trivial. We set a version number and a cache age on the http response of our entities:
X-Version: 23
Cache-Control: max-age=86400
When we receive the http response we process its body exactly as we might have done before but we do not store it anywhere. We leave that up to the browser to do that for us, and if we need the data a second time we simply re-request it, and it arrives almost instantaneous because the browser serves it from its cache.
And if we see an entity with an out of date version, then we immediately re-request it, being sure to override the cache by setting Cache-Control: no-cache in the request headers. This ensures we are always working with the current version of the entity.
this.http.get<T>(
url,
{...options, headers: {'Cache-Control': 'no-cache'}}
)
But how do we know a version is out of date? We are fortunate in that our versioning system is very granular, so we don’t get updates very often. Updates to current version numbers come via a web socket which I wasn’t involved with programing. So bear in mind that we are blessed, and this approach may not work for you (at least without putting some serious thought into it), and there are only two hard things in Computer Science: cache invalidation and naming things ;-)
Also some vigilance is required as there are a couple of ways I am aware of that the browser cache can be accidentally disabled which obviously now has terrible potential as a performance drain:
Inside the debugger, e.g. there's a "Disable cache" check-box in Chrome DevTools.
A bad SSL certificate. If the browser doesn't trust the server then it's right not to be retaining the data received. This is only a problem in a test environment where it's tempting to cheat and respond to "your connection is not private" by clicking "advanced" and "proceed".
So how's that for a solution (it's not going to make everyone happy is it)? Ngrx cannot be used as a bona-fide cache, but the browser can!
There are two main approaches i have found. They both involve adding a timestamp to any sections of your store that need to expire. The first is to check the age in the selector function and if it exceeds the limit you just dispatch the retrieval action. This one is fairly straight forward using the createSelector syntax and having access to a store for dispatch. The other requires you to dispatch the retrieval action as normal then you check the age of the data in the effect prior to retrieval. This can be achieved by combining the sttore observable with something like combineLatest(https://medium.com/#viestursv/how-to-get-store-state-in-ngrx-effect-fab9e9c8f087)
Example for first option
createSelector(previousSelector, (item) => {
const now = new Date();
const limit = new Date(now.getTime() - (5 * 60000));
if (item.age < limit) {
this.store.dispatch(new getFreshDataAction());
}
});
As far as I can tell, reducers change the state of the tree, mapStateToProps transform the state tree for the UI. However, the boundary is not clear. For instance, consider the example from the "Computing Derived Data" docs (http://redux.js.org/docs/recipes/ComputingDerivedData.html). See below.
My first instinct is to put the calculation of the visible todos in the reducer (and not mapStateToProps), that is, whenever a todo or the visibility filter changes, update the list of completed or active todos. This has several advantages:
No need for Reselect
Having all the logic in one place helps reduce the learning curve (when onboarding) and probably also makes it easier to test (since the integration tests for mapStateToProps are simpler, if non-existent).
On the other hand, 2) is subjective. So guidance on mapStateToProps would be helpful.
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}
const mapStateToProps = (state) => ({
todos: getVisibleTodos(state.todos, state.visibilityFilter)
})
const mapDispatchToProps = (dispatch) => ({
onTodoClick: (id) => dispatch(toggleTodo(id))
})
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
Update in response to #DDS:
To update multiple interrelated states based on one action means that these states can become out of sync... This could mean the visibleTodoList acquires items that don't exist in the original.
If by multiple interrelated states you mean visibilityFilter and todos, then as per the redux docs, one idiomatic solution is to refactor the state tree so that they are one state. There are other approaches mentioned in the docs as well. Certainly, as you allude, you now have the burden of ensuring the code to compute the derived state (visible todos) is always called. Once the code base gets large, a custom combineReducer (another idiomatic solution) that does additional transforms before returning the state makes sense.
Note that the code lives in separate files and the execution order of reducers is not guaranteed. Also, reducers don't normally have access to sibling state meaning they cannot derive data from it
See my comments above.
The example above may be contrived but the point is that to make the data stable, it's best to have a single source every component can rely on, and this requires the data to be normalized.
Yea, it all comes down to normalized vs denormalized state. I'm not yet convinced that normalized state is always the way to go...for the same reason NoSQL databases are sometimes the way to go.
Reasoning about more complex state [normalized and denormalized state] becomes difficult very quickly. This is why it is better to not put derived data in state.
Ah I see your point. Six months from now I may not see that visibleTodos is derived state (and so should be treated as readonly). Unexpected things will result.
NOTE: The are my two cents based on my personal experience, and not necessarily in line with best practices.
Redux state
This should be normalised, primarily because it makes writes (inserts/updates/deletes) easy to reason about.
Normalising redux state would mean that you should not be storing derived data in the redux state.
Derived data:
My personal experience of using react/redux (before the nice docs on http://redux.js.org/docs/recipes/ComputingDerivedData.html emerged) made me try to follow something that you (OP) are striving for: simplifying the places where code is written.
After accepting the normalization principle, the natural place for me to start writing derivation or 'state view' logic was in the react component's render function (which now sounds a little ugly). As code evolves, the render function becomes neater by creating derivation functions and keeping them outside the react component.
This creates an easy mental model for people working with the codebase:
redux-state: Normalized store
mapStateToProps: Just maps state to prop values in a dumb way
ReactComponent: Contains all the logic to 'view' the right pieces of the state and then render it. Further modularisation done as deemed necessary by author.
The reason Reselect is used over putting this in a reducer and in the state is analogous to why React is used instead of jQuery.
To update multiple interrelated states based on one action means that these states can become out of sync. Maybe one reducer interprets ADD_ITEM to mean "upsert" when another, coded in a different file months later by someone else, takes it to mean "insert with duplicates allowed". This could mean the visibleTodoList acquires items that don't exist in the original.
Note that the code lives in separate files and the execution order of reducers is not guaranteed. Also, reducers don't normally have access to sibling state meaning they cannot derive data from it.
The example above may be contrived but the point is that to make the data stable, it's best to have a single source every component can rely on, and this requires the data to be normalized. Storing derived data means storing the same data in multiple places and forms, but being interdependent.
Having a single source and having data flow unidirectionally, prevents disagreement on which data is authoritative.
The state should be thought of as clean-room data. It has a number of properties that make it dependable and easy to reason about:
All state is immutable, which means it can be stored anywhere and treated like a value without fear it will later be modified by external code.
All state is serializable which guarantees that it contains no code or cycles and can be shipped and stored easily.
It is normalized so it contains only one copy of each datum. There can be no desynchronisation or disagreement between different parts of the state. This makes the state harder to make internally inconsistent.
Reasoning about more complex state becomes difficult very quickly. This is why it is better to not put derived data in state.
Note that what may seem like a great and simple idea when you code it may trip you up years later when that little side project has blossomed into a highly sought-after essential tool. It's not hard to hack up working code on the first round so doing things the Redux Way is very much a future-proofing strategy.
I'm starting to read up on Redux and I like what I see so far. There is one area of concern that I have and it's async actions. I've seen how sagas, thunk, and others help in this area but I feel like I'm missing something.
I'm a fan of using FSMs to handle application logic and prevent things from getting out of sync, so coming to redux and not seeing something like this is a bit jarring to me.
I have an example in my mind that I keep coming back to that I want redux (or some plugin) to solve:
I have a login form. When the user presses login, an async call is made to a server. This is fine. But if the user presses login again, then another call is made to the server and the application state is now out of sync. I could add a state property that defines loggingIn, but this only works in this example.
Lets say I have a much bigger dependency. Lets say when a user logs in, an action is done to preload some data. And that data contains a preflight of more data to load. This now introduces a lot of if/else conditional logic and adds more information to the state. In a FSM, I would define multiple concurrent states for theses. Such as user:loggedIn, manifest:fetched, pageData:fetched. And each state would be a child of each other: user:loggedIn -> manifest:fetched -> pageData:fetched. So if a request was made to login, or refetch data, an error would be thrown because it's not a valid handler for the current state. How does one accomplish such complexity in Redux? Is it even possible?
Another example. Stemming off the Reddit API example. Lets say a user goes to a reddit post. I don't want the user to be able to submit a comment on the post before it's even loaded. I know this could be handled on the UI side with conditionals and such, but that seems really archaic.
I really appreciate any guidance.
Thanks!