Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
Disclaimer: This article is not an introduction to redux. There are a number of good resources to get started with redux (see below) and I suggest you begin with them. This article isnât about react either. The term âlifecycleâ is used in context of redux data flow and operations.
In just three years, redux has become quite a popular choice among frontend developers as the state management library of choice. Even more so in the react ecosystem.
Itâs amazing (and impressive, really) how small and simple the codebase is. And yet it can handle highly complex situations with easily. Perhaps this simplicity of redux is a major driving factor for its widespread popularity.
In the next part, weâll explore how middleware affect the redux lifecycle and the data flow.
The Basic Redux Lifecycle
Internally, redux only works with synchronous data flow and doesnât come with many âmoving partsâ or magic methods like some other solutions.
The most basic redux setup can be thought of as a single loop of state updates that is made up of just 2 components:
- The state tree
- The reducer function
Actions are accessory objects that are required to figure out how to update the state for the next iteration.
The reducer function takes in the previous state and the dispatched action as its arguments and returns the next state. If there are no changes needed, it returns the previous state as-is. Otherwise, it creates new state and returns it.
Reducer Decomposition
It is a common practice to split our state tree into multiple slices and write a separate reducer for each state slice. Actions may or may not be concerned with multiple state slices. This process of breaking our reducers into smaller and easier to understand pieces in a process called Decomposition.
Independent Reducer LifecyclesCombining Reducers
We can now combine several independent reducers back into a single reducer (often called the ârootâ reducer) and create a redux store using it.
import { combineReducers } from 'redux'
import fooReducer from './foo'import barReducer from './bar'âconst rootReducer = combineReducers({ foo: fooReducer, bar: barReducer,})
Alternatively, we could exclude the âReducerâ suffix from our imports to make the code lighter.
import foo from './foo'import bar from './bar'âconst rootReducer = combineReducers({ foo, bar,})
When an action is dispatched, the root reducer receives it and passes the same action object to all reducers for them to react to it independently. However, instead of passing the entire state tree to each reducer, redux will only pass the reducerâs exclusive state slice. The return value from each reducer is then merged back into the complete state tree.
This is the basic redux architecture and it is the most common one as well. With careful planning and smart division of the the app state most small-scale apps can work with this architecture without any added complexities.
State Dependent Reducers
As your state tree grows complex, you may find yourself in situations where you need to share state between different reducers. A quick fix is to combine those reducers into one. However, doing so usually creates fat reducers that donât scale well.
If we want keep our reducers separate, we need to figure out whether we need to share the current state slice or the updated version.
State Dependent ReducersâââCurrent State
Since reducers are just functions, we can pass them any number of arguments as we want. If a reducer requires state slice from another reducer, we can pass it in as the third argument.
Current-State Dependent ReducersCombining Reducers
We cannot combine dependent reducers using the combineReducers() utility that ships with redux.
Once you go past the core use case for combineReducers, itâs time to use more âcustomâ reducer logicâââRedux docs
Since reducers are just functions, we can pass in the state of a different reducer as a third argument.
import foo from './foo'import bar from './bar'âfunction rootReducer(state, action) { const fooState = foo(state, action) const barState = bar(state, action, state.foo)
return { foo: fooState, bar: barState, }}
Combining Current-State Dependent Reducers
State Dependent ReducersâââUpdated State
Generally, when a reducer depends on another reducerâs state, it requires the updated state. Personally, I have never come across a situation where the old state is required. If you know of any such experiences, do share them in the comments.
Updated-State Dependent ReducersCombining Reducers
The code looks almost the same as it was in the previous section. We have made only a small change. Instead of passing the old state in state.foo, we are now passing the updated state in fooState.
import foo from './foo'import bar from './bar'âfunction rootReducer(state, action) { const fooState = foo(state, action) const barState = bar(state, action, fooState)
return { foo: fooState, bar: barState, }}
Combining Updated-State Dependent Reducers
Dependence on updated state of other reducers means that there is a vertical hierarchy of reducer invocation. This doesnât mean much for trivial cases but the idea does hint the possibility of chaining reducers together. However, reducer chaining will increase the code complexity exponentially.
Note: Another alternative to the âshared-slice updatesâ issue would be to simply put more data into the action. You can read more about it here.
Putting it all together
Now that weâve seen some patterns of laying out reducers and combining them in isolation, letâs see what they look like in union. Hereâs a list of key things to observe:
- Each reducer receives its exclusive slice of state as the first argument
- Each reducer receives the same action object as the second argument
- Dependent reducers receive the complete state or other reducersâ state slice as subsequent arguments
- All updated state slices are merged together into a single state tree
- Dependent reducers can form an invocation chain
- Redux uses reducers to perform state manipulations
Addendum
The missing bits
In all of the drawings, I intentionally left out action creator(s) and the store to keep them clean and free from bloat. As you know:
- An action creator is a plain function that create an action object.
- The dispatch function accepts the action object and passes it to itâs storeâs root reducer.
- The store is the place where the combined state tree and the dispatch function live.
- Each redux store has its own associated root reducer that consumes the action object and updates the storeâs internal state.
- Beginnerâs guide to react/redux
- Getting Started with Redux
- Redux Basics
- Building React Applications with Idiomatic Redux
- The Complete Redux Book
Redux Lifecycle was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.