React Redux Data Flow and Redux Lifecycle Methods Overview
What is Redux and why do we need Redux
Redux is an open source JavaScript library which is designed for maintaining application state. Redux JS is a powerful and popular library nowadays. The command to install Redux library npm install redux --save.
Nowadays JavaScript single-page applications are very popular and powerful.
As the requirements for single-page applications have become complicated, our code must manage more state than ever before.
This state can include server responses and cached data, as well as locally generated data that has not yet been persisted to the server.
Front-end state is also increasing in complexity, as we need to manage active routes, selected tab, spinners, pagination controls etc.
That's why we need Redux.
click here to learn React Redux Architecture, folder structure.
Three Principles of Redux:
- Changes are made with pure functions
You write pure reducers to specify how the state tree is transformed by actions.Reducers are just pure functions that take the previous state and an action, and return the next state.Keep remembering to return always new state objects, instead of mutating the previous state.
import { combineReducers, createStore } from "redux"; const initialState = { text: "", completed: false }; function toggleReducer(state = initialState , action) { switch (action.type) { case "TOGGLE": return { ...state, completed: !action.completed }; default: return state; } } function todosReducer(state = initialState , action) { switch (action.type) { case "ADD_TODO": return [ ...state, { text: action.text, completed: false } ]; case "COMPLETE_TODO": return state.map((todo, index) => { if (index === action.index) { return { ...state, todos: { completed: true } }; } }); default: return state; } } const rootReducer = combineReducers({ toggleReducer, todosReducer }); const store = createStore(rootReducer);
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.
The state of your whole application is stored in an object tree within a single store.
This makes it easy to create universal apps, as the state from your server can be serialized and hydrated into the client with no extra coding effort.
A single state tree also makes it easier to debug or inspect an application, it also enables you to persist your app's state in development,for a faster development cycle.
- Click here to learn React JS Component lifecycle methods.
React Redux Data Flow and Redux Lifecycle Methods:
Basic Redux Data Flow and Lifecycle:
![]() |
Redux flow diagram |
Here, react component calls action creator, action creator returns an action, action sends information to the reducer, reducer creates a new state and that state sends to react component.
React Views
React Container component connect with Store to get the updated state or data. Map state to props (MapStateToProps), connected components specify what state they want as props. Container component dispatch an action.
Action Handlers
Most business logic and side effects are implemented here. This is the right place to handle the actions. Redux thunk and Redux Saga are the middleware to call the API. We'll discuss later about Redux Saga.
Store
Store holds the global app state, allows access to state via getState(), allows state to be updated via dispatch(action) and registered listener via subscribe(listener).
Redux JS Lifecycle Methods
Actions
Action is payload of information that send data from your application to your store. They are the only source of information for the store.
You can send them to the store using store.dispatch() or using react redux connect.
Action must have a type property that indicates the type of action being performed. Types should typically be defined as string constants.
//types const ADD_ITEM = 'ADD_ITEM' //object { type: ADD_ITEM, text: 'Item' }
Action Creators :Action Creators are functions which create an actions,It returns an action.
Redux JS Action Creators Example
types.js
const ADD_ITEM='ADD_ITEM'; const REMOVE_ITEM='REMOVE_ITEM'; export{ ADD_ITEM, REMOVE_ITEM }actions.js
import * as types from "./types"; const addItem = payload => { type: types.ADD_ITEM, payload; meta: { key: 'addItem' } }; const removeItem = (payload) => { type: types.REMOVE_ITEM, payload; meta: { key: 'removeItem' } }; export{ addItem, removeItem }
Actions Creators: dispatch() and connect() are the functions used to actions creators.
you can call function mapDispatchToProps() that receives the dispatch() method and returns callback props that you want to inject into the presentational component.
Redux JS Selector :selector is a layer in which we can modify store accordingly. This is good place to filtering and sorting the data.
selectors.js
export const getTodos=(state)=>state.todos;container.js
import React, { Component } from "react"; import { connect } from "react-redux"; import { addItem, removeItem } from "./actions"; import Todos from "./Todos"; import {getTodos} from './selectors'; class TodoContainer extends Component { constructor(props) { super(props); this.handleAddItem = this.handleAddItem.bind(this); this.handleRemoveItem = this.handleRemoveItem.bind(this); } handleAddItem(todo) { this.props.onAddItem(todo); } handleRemoveItem(id) { this.props.onRemoveItem(id); } render() { const { todos } = this.props; return ( <Todos todos={todos} onAddItem={this.handleAddItem} onRemoveItem={this.handleRemoveItem} /> ); } } const mapStateToProps = state => { return { todos: getTodos(state) }; }; const mapDispatchToProps = (dispatch, ownProps) => { return { onAddItem: todo => { dispatch(addItem(todo)); }, onRemoveItem: id => { dispatch(removeItem(id)); } }; }; //with mapDispatchToProps const enhance = connect(mapStateToProps, mapDispatchToProps); //without mapDispatchToProps const enhance = connect(mapStateToProps, { onAddItem: addItem, onRemoveItem: removeItem }); export default enhance(TodoContainer);
Reducers
The reducer is a pure function that takes the previous state and an action, and returns the next state.
Reducers specify how the application's state changes in response to actions sent to the store. Remember that actions only describe the fact that something happened, but don't describe how the application's state changes. NPM command to install react redux package npm install --save react-redux
reducers.js
const initialState = { todos: [], id: "", text: "" }; const todos = (state = initialState, action) => { switch (action.type) { case "ADD_ITEM": return [ ...state, { id: action.id, text: action.text, completed: false } ]; case "REMOVE_ITEM": return { ...state, id: action.id }; default: return state; } }; export default todos;
Create store and connect with React JS :
import React from 'react' import { render } from 'react-dom' import { createStore } from 'redux' import todos from './reducers' import Root from './components/Root' let store = createStore(todos) render( <Root store={store} />, document.getElementById('root') )
Redux Middleware
Redux thunk and Redux Saga are the middlewares . Nowadays React Redux developer followed Redux Saga which is very popular.
Redux Saga :
redux-saga is a redux middleware, which means this thread can be started, paused and cancelled from the main application with normal redux actions, it has access to the full redux application state and it can dispatch redux actions as well. I'll explain redux-saga in details in separate post.
sagas.js
import { delay } from "redux-saga"; import { fork, call, put, takeLatest } from "redux-saga/effects"; import api from "./api"; import { receiveData } from "./actions"; import * as types from "./types"; function* fetchResource() { const { data } = yield call(api.fetchData); yield put(receiveData(data)); } function* watchFetchResource() { yield takeLatest(types.FETCH_RESOURCE, fetchResource); } export function* rootSaga() { yield [fork(watchFetchResource)]; }Connect Redux-Saga:
import React from 'react'; import { render } from 'react-dom' import { createStore, applyMiddleware } from 'redux' import createSagaMiddleware from 'redux-saga' import rootSaga from './sagas' const sagaMiddleware = createSagaMiddleware() const store = createStore( reducer, applyMiddleware(sagaMiddleware) ); sagaMiddleware.run(rootSaga); render( <Root store={store} />, document.getElementById('root') )
Here, we learned react redux data flow, redux lifecycle methods, how they are connected to each other. Explained middleware redux saga, how to connect redux saga with react.
It was nice explaination of react and redux saga.Thank you so much.
ReplyDelete