Matthew Bodily
Lecture Slides for DevMountain's Web Development Course
Redux is an open source, state management library.
Redux was created by Dan Abramov, who also helped build create-react-app.
You can learn Redux directly from Dan here:
https://egghead.io/courses/getting-started-with-redux
Redux is a tool that allows us to manage an application's state in a more convenient way than needing to pass down/up through props.
The Redux pattern isn't exclusive to React.
There is a, however, a React Redux package that makes implementing Redux into React easier.
React Redux makes the Redux pattern even more useful by making the Store state available on props.
We'll be learning to use the Redux pattern with React Redux.
To set-up Redux/React Redux, we need to install the following packages:
npm install redux react-redux
Then create a redux folder inside of your src folder:
Redux set-up involves two kinds of files: reducer files (can have multiple), and a store file.
Reducers are where state can be stored, as well as actions to change the state.
The store makes the state from our reducers available to our application.
Reducer files will hold their own state, as well as action creator functions, and a reducer function that will work together to change the application state.
Their setup looks like the following:
const initialState = {
user: {}
};
const LOGIN_USER = 'LOGIN_USER';
export function loginUser(user){
return {
type: LOGIN_USER,
payload: user
};
};
export default function userReducer(state = initialState, action){
switch(action.type){
case LOGIN_USER:
return {...state, user: action.payload};
default:
return state;
};
};
initial state values
action creator function
reducer function
Action creators are functions that allow changes to the redux state.
They return an action object with two properties: a type and a payload.
The type is used in the reducer's switch statement, and the payload contains the new values to assign on state.
export function loginUser(user){
return {
type: LOGIN_USER,
payload: user
};
};
The reducer function contains a switch statement that helps change the state.
The action type returned from an action creator is used as the case for the switch, to determine how state should change.
export default function userReducer(state = initialState, action){
switch(action.type){
case LOGIN_USER:
return Object.assign({}, state, {user: action.payload});
// spread operator: return {...state, user: action.payload}
default:
return state;
};
};
The redux store is the place where we keep the application's state.
The store is a place other components can connect to in order to receive updates about information they might need.
The set up for a redux store is as follows:
import {createStore} from 'redux';
import userReducer from './reducer';
export default createStore(userReducer);
import {createStore}
import reducer file(s)
pass reducer into createStore
Now that the store and reducer are setup, we can now use React Redux to connect the store to our application.
To enable access to the store, we first need to wrap a Provider component around our application.
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import store from './redux/store';
import {Provider} from 'react-redux';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));
wrap App with Provider, passing in store as a prop
import store and Provider
Once Provider has been wrapped around the application, we can now pass information from our store state to any component that needs it by using a connect function, and a mapStateToProps function.
These two functions will work together to populate the props object of the component with information from the store state. This process is known as subscribing.
import React, { Component } from 'react'
import {connect} from 'react-redux';
class App extends Component {
render() {
return (
<div>
</div>
)
}
};
const mapStateToProps = (state) => state
export default connect(mapStateToProps)(App);
import connect
makes the redux store state available to the props object
pass mapStateToProps into connect
import React, { Component } from 'react'
import {connect} from 'react-redux';
import {actionName} from './redux/reducer';
class App extends Component {
render() {
return (
<div>
</div>
)
}
};
const mapStateToProps = (state) => state
export default connect(mapStateToProps, {actionName})(App);
import action creator
pass action into connect as the second argument
Note: If you aren't using redux state, but are using an action, the first argument of connect needs to be null.
When using multiple reducers, we need to 'bundle' them to make each of their states available on the store state.
We can do this using the combineReducers method that comes with the redux package.
import {createStore, combineReducers} from 'redux';
import userReducer from './reducer';
import carReducer from './carReducer'
const rootReducer = combineReducers({
userReducer,
carReducer
});
export default createStore(rootReducer);
redux-promise-middleware is a package that enables asynchronous code to work inside of redux.
This means we can make axios calls to a server from within redux.
To use it, install redux-promise-middleware from NPM:
npm install redux-promise-middleware
Then make the following changes to your store file:
// import the store and apply middleware
import {createStore, applyMiddleware} from 'redux';
import userReducer from './reducer';
// import promise middleware
import promiseMiddleware from 'redux-promise-middleware';
export default createStore(userReducer, applyMiddleware(promiseMiddleware));
Now that the setup is complete, we can make asynchronous code work in our action creators:
export function getRandomUser(){
const randomUser = axios.get('https://randomuser.me/api/')
return {
type: GET_USER,
payload: randomUser
};
}
Note: Promises have three states: Pending, Fulfilled, and Rejected. Redux-promise-middleware allows us to use these in our switch statement in the reducer function...
export default function userReducer(state = initialState, action){
switch(action.type){
case GET_USER + '_PENDING':
return Object.assign({}, state, {laoding: true});
case GET_USER + '_FULFILLED':
return Object.assign({}, state, {user: action.payload, loading: false});
case GET_USER + '_REJECTED':
return Object.assign({}, state, {errorMessage: action.payload});
default:
return state;
};
};
React-promise-middleware will append a promise-related status to any asynchronous actions that are fired from within redux.
By Matthew Bodily
React Redux