This gist is a resource for anyone who may be learning about Redux for the first time, or would like to learn more in depth about Redux and the Redux cycle. It will begin with a brief observation and explanation of the core tenets of the Flux application architecture. Next, it will dive into how Redux is a Flux implementation, examining its core defining features, and explaining why and when you may want to consider having it be a part of your client-side applications. Finally, it will conclude with a step-by-step explanation of the Redux cycle and how all the individual parts interact.
From the Facebook Github page on Flux - https://facebook.github.io/flux/
It essentially is a model, for which you to be able to better manage, test, and track issues related to state management when working within complex UI / client-side applications. The most important concept of the Flux paradigm is that data-flow is uni-directional, it only flows in one direction.
Depending on your familiarity with building React applications you may have some experience with this, where state that is held in class-based components is passed down to functional components via props, from parent-to-child.
The other core features of the Flux architecture for state management are:
Actions
Dispatcher
Store
View
Here is a diagram that the Flux docs recommend as the best example of all these core features and how they interact.
Actions are objects, that are sent to the dispatcher which inform it in how it should proceed. They are comprised of 2 parts: A type and a payload
Example action
{
type: 'ADD_TODO',
payload: {title: 'Practice React, id: 4}
}
Actions are generated by Action Creators as a result of a user interaction. An action creator is simply a function that constructs the action object and trigger a dispatch method within.
For instance, a ToDo List app would have actions generated whenever a user types in a new todo and submits it, and also when they delete a todo. Important to note: actions do not do anything themselves, they merely represent what action needs to be done via the type description and carry the necessary argument data (payload) for the function that will eventually execute the specified action.
The central hub that manages all data flow in a Flux application. It is essentially a registry of callbacks into the store and has no real intelligence of its own – it is a simple mechanism for distributing the actions to the stores. - Flux docs
The dispatcher is a transporter in essence; it takes the actions that are generated via user interactions or network requests and dispatches them to the store. One analogy may be to think of it as the conveyor belt you see at airports that transport your luggage. When you check into a flight and give your luggage to the ticketing agent, they apply a tag (type) to your luggage (payload), then they place it on the conveyor belt which takes your luggage and makes sure it gets to where it needs to be based on the information found on the tag!
The Store contains the application state and logic. THIS IS WHERE IT ALL GOES DOWN. The store(s) register with the dispatcher so that they can receive the actions, then through a series of switch cases they decide how to respond to the action. The switch cases will be based on the action types and when they match they will execute the logic contained within, that will update/mutate the data that is being held in the store. With this update to state, this triggers a re-render that will result in the views tied to that part of the Store to update to reflect those changes.
These are the components that render and display the data that is held in the store. They are also where user can interact with the application, which trigger actions, which are dispatched by the dispatcher, where they are received by the store and evaluated, resulting in callbacks being fired that manipulate the data, causing the views to be re-rendered.
So given that overview of the Flux pattern for data management, what is Redux and how does it differ?
From the Redux official site - (https://redux.js.org/)
taken from the Redux documentation
Single Source of Truth The state of your whole application is stored in an object tree within a single store
State is Read-Only The only way to change state is to emit an action, an object describing what happened
Changes are made with pure functions To specify how the state tree is transformed by actions, you write pure reducers
In Flux applications you could theoretically have multiple stores, with each store being responsible for that data in its domain.
If you have a ToDo application you would have a store responsible for all the todos, its domain. That store would handle adding new todos and deleting out old ones. But let's say you needed to login in order to use that ToDo app, you would most likely want to create another store that would handle that user information (their name, id, history of ToDos) and update when a user logs in or logs out. By this concept, you have a separation of concerns which makes your application more modular.
In Redux there is only ONE store
The way you are able to still have these separation of concerns is through Reducers
Whereas in the Flux diagram the logic that goes on to update and mutate the state is contained within the store, in Redux that same logic/functionality is teased out and represented by Reducers.
Each reducer is responsible for its particular domain of state, and when actions are dispatched they pass through all the reducers, and only for the particular reducer, with the particular switch case, does a method execute resulting in a change of state. This is known as reducer composition
Example of a reducer:
export const todos = (state = [
{title:'Buy Groceries', id: 1},
{title:'Practice React', id:2},
{title:'Drink more Coffee', id:3}], action) => {
switch (action.type) {
case 'ADD_TODO':
return [...state, {title: action.todo.title, id: action.todo.id}]
case 'REMOVE_TODO':
let newTodos = action.payload.todos.filter(todo => todo.id !== action.payload.id)
return [...newTodos]
default:
return state
}
}
That first parameter of the todos reducers is declaring the default value of our applications state. When our applications first boot up the reducers set the default values of state in the store.
Example of action objects for context:
export const addTodo = (todo) => ({
type: 'ADD_TODO',
todo
});
export const removeTodo = (id, todos) => ({
type: 'REMOVE_TODO',
payload: {id: id, todos: todos}
});
Reducers are what are known as pure functions, which means they have no side-effects and given an input they will always provide an expected output
This makes testing your Redux applications extremely easy, and is one of the reasons Redux is so popular.
Action Creators
In Flux applications Action Creators typically invoke the dispatch method within themselves, in Redux applications the result of the Action Creator (the action object) is passed to the dispatch method as an argument. This is done via the mapDispatchToProps function (a naming convention for a function you create) which is created in the component tied to the specific user interaction. The dispatch method is passed through mapDispatchToProps, which will take the result of the action creator function and send it to the reducers.
Immutability of State
In Redux it is highly discouraged to mutate state. If for instance you have an array of todos, in Flux you would be mutating the state directly. Redux, instead, operates by creating a brand new version of state entirely. So rather than appending directly to the array of todos, a Redux application would spill out the contents of the previous state (using the spread operator) into a brand new array that would then become the current version of state.
This gives Redux applications the amazing ability of TIME TRAVEL. Via the Redux DevTools you are able to go and track all the changes that occur to your application's state and have a record of all of them. In this sense, each action object is like a transaction record that correlates to a change in the applications state.
In many cases, Redux may be overkill if your application is fairly simple. The distinction on when and why you should use your application can be evaluated with the following points:
- User workflows are complex
- Your app has a large variety of user workflows (consider both regular users and administrators)
- Users can collaborate
- You’re using web sockets or SSE
- You’re loading data from multiple endpoints to build a single view
Source: (https://medium.com/javascript-scene/10-tips-for-better-redux-architecture-69250425af44)
The Redux cycle operates similarly to the Flux cycle described earlier, but here is a more detailed diagram illustrating all the steps that go into an update of state in a Redux application.
- User interaction triggers an Action Creator which creates an Action Object
- A function provided by Redux called dispatch is invoked with the result of the action creator (the action object)
- The dispatch method sends the action object to ALL the Reducers
- The reducers evaluate the action object and if it pertains to their domain of state they execute logic that returns a new state. Otherwise they return the previous state.
- A change in state is updated in the Store which triggers a re-render and the application reflects the new state