Without redux-thunk, your action creators can still take dispatch as an argument and seemingly do whatever a thunk would:
https://stackoverflow.com/a/33892505/378622
So what's the motivation for redux-thunk, exactly?
Dan Abramov's answer here How to dispatch a Redux action with a timeout? explains it in detail.
This summary is probably not complete, but my take from it is:
No need to have dispatch as a prop in components and passing it around
Async control flow is more convenient, you can treat other actions like promises without having to wrap them in new Promise() yourself
Additional argument getState in actions to read from state
Related
I am fairly new to redux-toolkit but I am quite partial to using their slices. They seem like an easy enough way to create reducers.
I do not know, however, how to execute async operations after an action has been dispatched. My use-case is, that I need to update the state and then execute an async operation (or more generally an operation with side-effects).
How would one do this?
Use redux saga for that. It will basically be listening to your action and call the async function whenever action is dispatched
As redux-saga describes itself as
a library that aims to make application side effects (i.e.
asynchronous things like data fetching and impure things like
accessing the browser cache) easier to manage.
The select effect is just used to get a slice of the current Store's state. It doesn't produce any side effect at all (no I/O operation, no mutation,etc). It's just a purely functional operation. Why a purely functional operation was designed to be an effect ?
Because none of your saga code is supposed to be interacting with the store directly overall. Whatever your saga needs to do, whether it be making an AJAX call, dispatching an action, or anything else, gets done by yielding an effect description and asking the middleware to do that work for you. Your saga doesn't have access to the store directly to call dispatch(), so there's no reason for it to have access to getState() directly either.
I use react + redux with multiple reducers.
I figured out that every dispatched action goes through every reducer, although there's only one relevant reducer for this action.
Is there an option to force every dispatched action to go only to its reducer?
If not - why? Surely I misunderstand something.
I do not have enough points to mark it as such, but this looks like a duplicate, please see:
Why a dispatch to a reducer causes all reducers get called?
All reducers will be invoked when an action is dispatched?
Short answer: it is normal and intended, there is no such option to prevent it because when you have a complex state tree, you should not have to care to compute and check what needs to be changed manually, this is the role of redux and unless you have a huge application, performance will not be an issue (reducers are often just simple multiple lines pure function).
If you are worried about performance, check out https://github.com/reactjs/reselect mentionned in one of the other question.
Per the Redux FAQ entry on "calling all reducers", you really only have one root reducer function. If you generated that root reducer using the built-in combineReducers function, then yes, it will always run all of the slice reducers you've provided for every dispatched action, because that's how combineReducers works.
If you want other behavior, you can write your own reducer logic for your specific use case. See the Redux docs section on "Structuring Reducers" for more information on various ways to organize reducer logic.
I'm learning redux and am struggling to understand why state has to be immutable. Could you provide me with an example, in code preferably, where breaking the immutable contract results in an not so obvious side effect.
Redux was originally invented to demonstrate the idea of "time-travel debugging" - being able to step back and forth through the history of dispatched actions, and see what the UI looks like at each step. Another aspect is being able to live-edit the code, reload it, and see what the output looks like given the new reducer logic.
In order to be able to properly step back and forth between states, we need to make sure that reducer functions have no side effects. That means data updates need to be applied immutably. If a reducer function actually directly modifies its data, then stepping back and forth between states will cause the application to behave in an unexpected fashion, and the debugging effort will be wasted.
Also, the React-Redux library relies on shallow equality checks to see if the incoming data for a component has changed. If the data references are the same, then the wrapper components generated by connect assume that the data has not changed, and that the component does not need to re-render. Immutable data updates means that new object references are created, and thus connect will see that the data has changed and the UI needs to update.
Two Ideas
There are two ideas about immutability that you need to understand:
Mutate the state only in the reducers
Using an immutable data structure
Mutate the state only in the reducers
Redux tries to ensure that you only mutate the state in the Reducers. This is important because it makes easier to think about your application data flow.
Let's say that a value is not displayed in the UI as you expected or that a value that should have changed still showing its original value.
In that case, you could just think about which reducer is causing the mutation and see what went wrong.
This makes thinking about Redux issues very simple and makes developers highly productive.
Sometimes you can mutate the state in a view or in an action by mistake. If you think about the life-cycle:
Action -> Reducer -> Views-> Action
If the state changes in a view and then an action is triggered the state change could be override. This would make very hard to find out what is going on for developers. We solve this by mutating state only in the reducers.
Note: another nice thing is that reducers are pure functions and all the async stuff takes places in the actions/middleware. So all side effects take place in the actions/middleware layer. Actions are our connection with the external world (HTTP, Local Storage, etc.).
Using an immutable data structure
As I have already mentioned, sometimes you can mutate the state in a view or in an action by mistake. To reduce chances of this happening we can make state mutations explicit by using and immutable data structure.
Using immutable data structures also has performance benefits because we don't need to perform deep equality checks to check for mutations.
The most commonly used provider of immutable data structures is immutable.js.
I think the key ideas would be
Easy to replay any given situation/test flow - you can replay with the same actions starting from the same initial state
Fast component re-rendering - because the reference is changed and not its values, it is much much faster to test the change
Of course, there are other aspects and I suggest this nice article which explains in more details.
Maybe someone can explain this to me, but these two principles, in my opinion, contradict each other and it's really hampering my progress in learning Redux.
State is read-only and Changes are made with pure functions
The first line after State is read-only is this: "The only way to mutate the state...". If something is read-only then it shouldn't be able to be mutated in the first place. Even in the Egghead videos the states are always labeled as const, but are still changed defeating the purpose of const.
Can someone please explain to me how these are not diametrically opposed ideas?
There's no contradiction, though certainly the confusion is understandable.
A good way to think about it is to draw an analogy between React state and Redux state.
In React, you only ever define your component state once, in the constructor() or getInitialState() method. From that point on, you always treat this.state as read-only, you never assign anything to this.state. To change the state, you call this.setState().
The same is true in Redux. You define the state - or each different part of the state - once. Typically it's called initialState and assigned as the default value of the state parameter of your reducer. Treating state as read-only means never overwriting or mutating the state. The only way to change the state is with the return value of your reducer.
Or, in short...
I think your real hang up is over the state being both changeable and read-only, so let me be more pithy:
A CD-ROM is read only, but I can eject the CD and replace it with another.
The state is read-only, as far as the world outside Redux is concerned. Think of it as a view on the append-only sequence of application events (actions) that have happened. You can generate new actions, and those will result in the state becoming different.
Of course, it's the real world and there is mutability somewhere. As Rich Hickey famously put it:
“If a tree falls in the woods, does it make a sound? If a pure
function mutates data to produce an immutable value, is that ok?” ~
#richhickey
But as much as is practically possible, you write your application without directly mutating things. Instead, you describe the changes you would like to see (dispatching actions), and a framework (Redux) carries them out, using your reducers to create the entire view of the store (the state) at once, and through React's props, a new view is generated as a result.
This is inspired by the concept of functional reactive programming. In this paradigm, all functions are pure, and you handle the concept of change over time by representing "what you would do with a value, if it were to have changed". In some sense, you conceptualize the entire past and future of a variable as an immutable function, whose future you simply happen not to know yet. Sorry if that got too philosophical.