“Redux” is a predictable state container for JavaScript applications. It is simply a state management tool.
Component Oriented Architecture - Writing applications using React means creating components. Components work like new DOM nodes. We build our app out of a tree of Components, nested one inside the other.
In React, we manage states for individual components but Redux comes into picture when we need to manage our state at the application level rather than component level. The application state can be used by any of the components in the application if that component is connected to the Redux Store.
Redux has a central store that consists of the state of the application. Components connected to the store will be able to access the data from the store without having to send down the data via props from the components which don’t need the data.
Don't use Redux until you have problems with vanilla React. ~ Dan Abramov
Why do we need Redux?
When working in small projects with simple tree structure, it is easier to maintain the states of the components and pass the data between the components. For this, the data needs to reside in a single component so that it can be passed on the sibling components. The method(s) needed to update the state also need to reside in the parent component and passed to the sibling components as props.
The diagram shows that if the data is needed by the child component (6), then it needs to be passed through the components in between (2nd and 4th ) irrespective of whether this is required by those components or not.
Image Ref: https://www.smashingmagazine.com/2016/06/an-introduction-to-redux/
But what if the tree gets complex? For passing data in components that are far apart in the tree, do we still need to pass the data to all the components in between the parent component and child component which needs the data?
The state will have to be lifted to the nearest parent component and to the next until it gets to an ancestor that is common to both components that need the state and then, it is passed down.
So, conclusively, state management gets messy when the app gets complex. Here comes the need to use state management tool such as Redux.
The diagram shows how the Redux Store will be used to pass the data to the components.
Image Ref: https://www.smashingmagazine.com/2016/06/an-introduction-to-redux/
Taking a real-time example of a situation when we will need to use the Redux for managing the state of the application.
Let's take the example of the Facebook profile. When you update your profile picture from your Edit Profile page, on saving, it should also update the profile picture wherever it is used in your profile. By using the approach of simple React, there needs to be some relation between the component of edit profile and other components so that the image updates everywhere. If we use this approach, it would be messy to maintain the structure of the project and route the data passed in the components.
Three Principles of Redux
These principles are true from some point of view but not from all points of view.
- 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 the 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.
Understanding how Redux works
Image Ref: https://github.com/reduxjs/redux/issues/653
Data flow in Redux is unidirectional. To make the data flow more predictable and consistent, the data follows a lifecycle pattern in Redux.
Briefly explaining the flow of redux
We have a view which is we can say our React component(s) connected to our store which has an object called state and which sends it down to the component. When we interact with our component for example, when a button is clicked, action will be fired. So, action creators dispatch an action to the store. Reducers are pure functions which specify how the application state changes in response to that action, and reducer than respond with the state change.
As the application state is immutable, a new state is recreated rather than updating. The state is sent down to the component and the component reacts accordingly that reflects our Views or Components.
Terminologies used in redux
State
A state is a JavaScript object. It can only be updated by dispatching an action.
Actions
Actions are the only way by which we can send the data to the store. Actions are a plain JavaScript object that describes the change.
An action object must have type which indicates the type of action being performed and other properties of the object can be added as required.
Example
- {
- type: DEMO_ACTION,
- payload: “Data to be passed”
- }
We can write actions in ways like:
- export const demoAction = () => dispatch => {
- dispatch({
- type: DEMO_ACTION,
- payload: “data”
- })
- };
This action will dispatch the action to type DEMO_ACTION and we are passing the payload with the type property.
Action Creators
Action creators are the functions that create actions, or we can say functions which return action.
Example
- function demoAction(payload)
- {
- return {
- type: DEMO_ACTION,
- payload,
- }
- }
Dispatch()
Dispatch function lets you dispatch an action to change the state of your application. For understanding the working of dispatch function we can pass the result of the action to the dispatch() to initiate it.
Example
- dispatch(demoAction(payload))
When using the helper like connect() of React-Redux, you can use bindActionCreators() to automatically bind many action creators to a dispatch( ) function.
Example
- export function demoAction(payload) {
- return
- {
- type: DEMO_ACTION,
- payload
- }
- }
Reducers
Reducers are simply pure functions. A function that calculates the next state tree based on the previous state tree and the action being dispatched.
It's called a reducer because it's the type of function you would pass to Array.prototype.reduce(reducer, ?initialValue)
Redux will call our reducer with an undefined state for the first time.
Example
- function demoApp(state = initialState, action) {
- switch (action.type)
- {
- case DEMO_ACTION:
- return Object.assign({}, state, { visibilityFilter: action.filter })
- default:
- return state
- }
- }
Store
Store is just an object which has few methods. An application has a single store, which can further be decomposed rather than multiple stores. Store can be created using createStore( ).
Example
- import { createStore } from 'redux';
- import demoApp from './reducers';
- const store = createStore(demoApp);
For making our store available to our application components we need to pass that store in the provider as shown below. We need to make these changes in our index.js file so that store is available to our application.
Example: (index.js)
- import React from "react";
- import { render } from "react-dom";
- import { BrowserRouter as Router } from "react-router-dom";
- import App from "./app/App";
- import store from "./app/configureStore";
- import { Provider } from "react-redux";
-
- render(
- <Provider store={store}>
- <Router>
- <App />
- </Router>
- </Provider>,
- document.getElementById("app")
- );
-
- Connect()
Connect is a function provided by the React-Redux which helps us to connect the React component with the Redux store.
The connect function doesn’t modify the component passed, it just wraps the component passed and return the new connected component class, which helps us to get the pieces of data passed and the functions it can use to dispatch actions to the store.
Example
- Import ….
- class componentName extends Component {
- }
- export default connect(mapStateToProps, mapDispatchToProps)(componentName);
Here, the first argument passed to connect function, mapStateToProps selects the part of data from the store that the React component needs as the state of the store changes. It is a simple function that returns an object which the component needs.
MapDispatchToProps is a function that lets you dispatch an action when that function is called.
Summary
Both the values in mapStateToProps and mapDispatchToProps are received by the component as props for the component.
To refer to the next part of the article please visit the link provided below for second part of the topic. The article explains how to set up our solutions when we want to learn a step further in React, which is communication between components. It also contains two separate demo projects for clear understanding.