Use React with Redux, and be Amazed!
Introduction
When we develop react apps we are using state and props to build data pipelines. But as the number of components grows, managing data with props becomes a very difficult task. That’s when Redux comes into play.
We have to spend some effort in order to set up redux, but when we are done, data management becomes much easier.
The main elements of redux are the store, reducers, actions and components. The store stores data. A component can subscribe to the store for updates. From inside a component, we can dispatch an action to update the store. The action generates a return value, which is sent to the reducer. In the reducer, we define the store update based on the type of action, and the store updates.
How to Install Redux
Before using redux, we need add redux and react-redux libraries to our app using these commands:
npm install --save redux
npm install --save react-redux
Setup
Next, in our index.js, we need to create the store. The store is exactly what it sounds like: we use the store to store all data in our app so that we can call it when we need it.
First, we import a Provider component and createStore and applyMiddleware functions:
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
Second, we call createStore:
const store = createStore(reducer, applyMiddleware())
Third, we use a Provider component to connect the store and our app:
ReactDOM.render(<Provider store={ store }><App /></Provider>, document.getElementById('root'));
Combined, the index.js file looks like this:
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';const store = createStore(reducer, applyMiddleware())ReactDOM.render(<Provider store={ store }><App /></Provider>, document.getElementById('root'));
Reducer
When we call createStore we have to use a reducer. But we don’t have one yet! As we mentioned earlier, a reducer is responsible for updating the store.
In a reducer, we can use an if statement, but I prefer to use switch/case syntax. Every time the type of action matches with a reducer condition, the store updates.
This is how our reducer looks:
const initialState = {
token: null,
userId: null,
errors: []
}const reducer = (oldState = initialState, action) => {
switch (action.type) {
case "LOGIN":
return {
...oldState,
token: action.user.token,
userId: action.user.userId,
errors: action.user.errors
}
default:
return oldState // what's old is new again
}
}export default reducer
Action Creator
The action creator creates and dispatches our actions. When we dispatch an action, it is very important to assign the action the same type we assigned in the reducer condition so that the reducer can recognize which action was dispatched.
Here is an example of an action function inside an action creator:
export function loginAction (userData) {
return (dispatch) => fetch('http://localhost:3000/login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
username: userData.username,
password: userData.password
})
})
.then(r => r.json())
.then(resp => {
if (resp.errors){
localStorage.clear()
}
else {
localStorage.token = resp.token
localStorage.userId = resp.user_id
}
dispatch({
type: "LOGIN",
user: {
token: resp.token,
userId: resp.user_id,
errors: resp.errors
}
})
})
}const actionCreator = {
loginAction
}export default actionCreator
Connect
Finally, we need to subscribe a component to the store, so the component re-renders when the store updates. First, we need to actually connect the component to the store in the same place where we exported the component:
export default connect(mapStateToProps, {loginAction}) (App)
Also we need to use mapStateToProps to extract data from the store to the component props. We do not have to extract the entire store. Instead, we can specify which data we need in the return value of the mapStateToProps function:
const mapStateToProps = state => {
return {
token: state.token,
errors: state.errors
}
}
Conclusion
When I first started using redux, I felt like it was not necessary and too complex. But as my apps became bigger and bigger, redux became very handy. After setup was complete, I was able to manage data much faster and easier using redux. You can, too!