Learn React-Redux in 7 minutes
Redux is a state container for JavaScript apps. It can be used with any Js view framework / Library . But it’s most popular because of its usage with ReactJs.
Why use redux . explain it to a 5 years old ! (well not exactly 5 years.. )
You have ComponentA (A button), and on it’s click you need to change the color of a componentB (div with text) , where CompA and CompB don’t have a parent child relationship . Redux will have a central object (store) , and when compA will try to update the color of compB it will update the `store` with new color value and also ask compB to re-render with new color.
So compA and CompB won’t talk directly , redux will facilitate that . That’s redux for you , ‘communication facilitator using central store object’.
Lets go for a run . Embrace for impact .
- You need to create a store — (store.js) . Redux has a single store that holds the entire application status, and it looks like.
// redux central store looks like
{
router:{
...
},
colorA: 'green'}
You can’t create the store manually like `store={}` , you need to use `createStore` method of Redux in order to do that .
//store.js
import { createStore } from 'redux';
import rootReducer from '../reducers/rootReducer';const store = createStore(rootReducer);
export default store;
All the imported modules are npm packages, except from reducers. So we will discuss reducers next.
2. Reducer: You can’t update the store object by yourself . like store.colorB = ‘whatever’ . You need to use reducer. So your **reducer name (colorB) is the the store property name you need to change and **`payload` is the value
// colorReducer.js
export function colorB(state = 'green', action = null) {
const {type, payload} = action;
switch (type) {
case 'myActionName_UpdateColor':
return payload;
default:
return state;
}
}
Above code is just a function , it’s not a redux reducer yet . For each store property you need different reducers . Then you combine all these reducers to create final redux reducer.
Your Reducer function should be a pure function means given a particular set of input it’ll always return same out-put.It won’t modify the passed params (here `state`) and won’t make any network calls / perform dom manipulations etc., instead return a new object with modified value.(If you didn’t understand this paragraph , it’s ok , move on :) )
//rootReducer.js . Imported in store file above.import { combineReducers } from 'redux';
import { colorB } from './colorReducer.js';const rootReducer = combineReducers({
colorB
});
export default rootReducer;
3. Now your componentA (the button) wants to change the color of component B . So it will have to update the store and it needs to ask the reducer to do it . But component can’t just ask reducer directly . It need to dispatch an action . Based on that action Redux framework will ask the reducer to update store.
// colorAction.js . Notice action type is the same string that's
// been used in reducer case . and this function name will be used // by componentA to call this.export function changeColor(color) {
return {
type: 'myActionName_UpdateColor',
payload: color
};
}
Again this is a simple function(Action) . I don’t see anywhere it’s asking any reducer to do anything . Well we need to pass it do react-redux connect function and react-redux will make it a dispatch-able function that can update the store and make that function available to connect’ed component’s props list.
Component A
//ComponentA.jsimport React, { Component } from ‘react’;
import { connect } from ‘react-redux’;
import * as colorActionCreator from ‘../actions/colorAction’;class ComponentA extends Component {
render() {
return (
< div >
<button onClick= {() => {
const randomColor = ‘#’ + Math.random().toString(16).slice(-6);
this.props.changeColor(randomColor);
}}>
Change Color
</button>
</div>
);
}
}export default connect(
null,
colorActionCreator
)(ComponentA);
Boys and girls . Ladies and gentlemen . Please pay attention. Following is the most important part of this article.
connect is a method from react-redux library . `connect` above is enabling this React component to get connected to Redux store . connect takes 4 params ( 2 will discuss only 2 here , rest 2 are beyond scope.)
A. 1st param is a function that returns an object like {colorB: state.colorB}, whose each key is the central - store property that this component is listening to (*later. Read on..) . Iff that property value changes in the central store , redux will re-render this component and make the updated value available in props.
why is it null here then ?
Now compA , is not listening for any store property change . It only tries to change the store property , it doesn’t know who is compB, and more importantly what happens to application after that property change it doesn’t care. so 1st property of @connect is null here.
B. 2nd param is your Action function(s) that you want to use in your component . As I said earlier , your own written action function ain’t calling the reducer directly .So we need to pass this function to @connect React-redux library, which will make that function a dispatch-able function. And make the modified function(who can talk to reducer) available to your props with the same name.
Here’s an interesting thing , if you set a debugger inside render , and try to see how this.props.changeColor function looks like the following …
//console.log(this.props.changeColor)
function() {
return dispatch(actionCreator.apply(undefined, arguments));
}
Now the second pair of braces after connect doing ?
If a function returns another function, that’s the syntax to immediately call the returned function. React-Redux’s connect method sends back a function (named `wrapWithConnect`) and that returned function is called back immediately with your component (here ComponentA) as param.
Component B
//ComponentB.js
import React, { Component } from ‘react’;
import { connect } from ‘react-redux’;
import React, { Component } from ‘react’;
import { connect } from ‘react-redux’;class ComponentB extends Component {
render() {
return ( < div >
<div style = {{color: this.props.colorBinProps}} >
I have many colors.
</div>
</div>
);
}
}
export default connect((state) => {
return {
colorBinProps: state.colorB
};
})(ComponentB);
So here connect has only 1 property .
A. 1st param should be a function that just returns an object . And each key value pair of that returned object represents the individual store property , this component is listening to . ComponentB is interested in store’s colorB property . When store’s colorB property-value is changed , compB will be re-rendered with new value, available as colorBinProps in props.
React-Redux ensures that This compB is re-rendered only when colorB property of store (subscribed property) is changed. Other store property changes wont affect this component at all .
why only one param here in connect ?
This component is just listening to the store’s colorB, and it got no ambition to change any store property and become a smart dude ( I mean smart component) . So it requires no ‘action’ , hence there is only one param passed to connect .
If I were successful in describing what I tried to describe , then that’s it . You understand React-Redux now .
Not so important . Have time ? Read on (Connecting the dots…)
One thing I deliberately skipped initially. We created the store file at the top. This store should be available to every connected Comp. But no where it’s being passed or imported .
then how store is available everywhere ?
It’s used in only one place main.js(entry point of the App) , and from there it’s available everywhere .Entire App is a child component of Provider and store is passed as props to child . (Under the hood, It’s done via React’s Context ).
//main.js
import 'babel-core/polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import store from './lib/store';
import IndexPage from './components/indexPage';render(
<div>
<Provider store={store}>
<div>
<IndexPage/>
</div>
</Provider>
</div>,
document.getElementById('content')
);
And finally here’s the indexPage which holds the componentA and componentB.
import React from ‘react’;import { componentB, componentA } from ‘components’;export default class IndexPage extends Component{
render(){
return ( < div >
<ComponentA/>
<ComponentB/>
</div>
);
}
}