Redux is a state management library for JavaScript apps. The core Redux library implements the flux pattern. In this post, I won’t be writing about the Redux library but the react-redux
library which has the plumbing code to integrate Redux into React apps. When we use react-redux
library, the plumbing code that makes it work is hidden from us. Without knowing how it works, we may not know how to optimize our apps.
Redux store
We will use create-react-app
or CRA to create a new app. And add redux
.
create-react-app reduxdemo yarn add redux
We will create a store which has a number. A reducer named countReducer
updates the number when it receives an INCREMENT
action. Create a new file named countReducer.js
in the src
folder.
export default function countReducer(state = 0, action) { switch(action.type) { case 'INCREMENT': return state + 1; default: return state; } }
Create the store in another file named store.js
.
import countReducer from './countReducer'; import { createStore } from 'redux'; export default createStore(countReducer);
Store has three functions:
- getState() to get the number.
- dispatch() to dispatch an action.
- subscribe() to provide a callback when an action is processed.
We will use all three functions.
Integrate Redux with React
Open index.js
which renders the App component in the document. Import the store.
import store from './store';
Move the render into a separate function.
function render() { ReactDOM.render(<App />, document.getElementById('root')); } render();
Now comes the surprising part. When the store updates the state, we want to call the render function.
store.subscribe(render);
Would you believe it? Every time the store handles an action, we render the App
component. The entire component tree is re-rendered. This is not quite obvious when we use react-redux
. Provider
component of react-redux
does it under the covers. But there is more.
When we use connect
of react-redux
, there is some more magic. Modify the render function like so.
function render() { const count = store.getState(); ReactDOM.render(<App count={count} />, document.getElementById('root')); }
We get the state stored in the store. And pass it as a prop to the App
component. The connect
part of react-redux
does precisely that.
- Get the state from the store.
- Call
mapStateToProps
to get the props. - Return a new component with props attached to the wrapped component.
Optimize performance
Our component tree is re-rendered every time when the store processes an action. Let’s test whether it is true. Open App.js
. Add a count variable that will track the number of times the component renders.
let count = 0; class App extends Component {
Display both the module count variable as well as the count prop in the render function.
<p className="App-intro"> <div>render count: {++count}</div> {this.props.count} </p>
Add two buttons. One to increment the count. And another to dispatch an arbitrary action.
<p className="App-intro"> <div>render count: {++count}</div> {this.props.count} <button onClick={() => store.dispatch({ type: 'INCREMENT' })}>+1</button> <button onClick={() => store.dispatch({ type: 'Xyz' })}>xyz</button> </p>
After clicking the +1
button three times and xyz
button three times, our app should like so.
It is easy to explain. Whenever the store handles an action, it re-renders the entire component tree. So, our App
component is rendered 7 times. Please note that we dispatch an arbitrary action of xyz
type. Even that re-renders the component tree. We should optimize our App component to avoid unnecessary re-render.
We do this by extending App
component from PureComponent
. PureComponent
tests if props have changed and renders only when the props have changed.
class App extends React.PureComponent {
Clicking +1
button thrice and xyz
button thrice produces the desired result.
PureComponent
checks if the new props are equal to the old props. It renders the component only if the props are different. Thankfully, connect
of react-redux
emulates the behavior of PureComponent
. It re-renders the wrapped component only if the new props are different from old props.