Redux was created by Dan Abramov, who also helped build create-react-app. Redux is a state management tool that allows us to manage an application state that components can subscribe to to access that state.
Redux isn't specific to React, but there is a React Redux package that makes implementing Redux into React easier. React Redux better follows the structure of React, making the application state available as props.
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 state, as well as actions to change 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 Object.assign({}, state, {user: action.payload});
default:
return state;
};
};
state values
action builder
reducer function
Action builders are functions that allow changes to the redux state. They will return an object with two properties: a type and a payload. The type is used for the reducers switch statement, and the payload is generally new values to put 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 from action builders is used as the case for the switch, to determine how state is changing.
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 used to make state available to our application. 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 connect with our application, we first need to wrap Provider 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 connect our redux state to any component that needs it by using connect, and mapStateTo Props. This will make selected items from redux state available as props for the component. This process is known as 'subscribing'.
You right now -->
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 state available as props
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
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.
redux-promise-middleware is a package that allows asynchronous code inside of redux. This allows you to make requests to your server. 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));
Once those changes have been made, you can make asynchronous code in your action builders:
export function getRandomUser(){
const randomUser = axios.get('https://randomuser.me/api/')
.then((res) => res.data.results[0]);
return {
type: GET_USER,
payload: randomUser
};
}
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;
};
};
When using multiple reducers, we need to 'bundle' them to make all of redux state available in one place. We can do this using the 'combineReducers' method from redux.
import {createStore, combineReducers} from 'redux';
import userReducer from './reducer';
import carReducer from './carReducer'
const rootReducer = combineReducers({
userReducer,
carReducer
});
export default createStore(rootReducer);