React and Redux can get pretty complicated and appears very complicated at the beginning. The following is an attempt to create a very simply introduction to adding Redux to your React app. We will create a class component and a functional component using the Redux store.
Add the following packages to package.json and run npm install.
"react-redux": "7.2.3",
"redux-thunk": "2.3.0",
"redux": "4.0.5",
Update index.js with the following imports and render.
//redux
import { Provider } from "react-redux";
import store from "./redux/store";
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
Update App.js with a component that will use the redux store
import Users01 from "./components/users01";
import Users02 from "./components/users02";
function App() {
return (
<div className="App">
<h1>React Testing</h1>
<Users01 />
<hr />
<Users02 />
</div>
);
}
export default App;
Create /components/users01.js (functional component)
import React, { useEffect } from 'react';
//redux
//useSelector, useDispatch only works in functional components
import { useSelector, useDispatch } from "react-redux";
import { users_list, users_add } from "../redux/actions/usersActions";
export default function Users01()
{
//this is the item in the store you are using (state.users.items)
const users = useSelector(state => state.users.items);
//this is used to call an action (populate the users in this case)
const dispatch = useDispatch();
useEffect(() =>
{
//on initial load, call the users list action
dispatch(users_list());
}, []);
return <>
<button onClick={() => dispatch(users_add())}>Add User</button>
<div className="users">
{users.map((item, index) => (
<h1 key={item.name}>{item.name}</h1>
))}
</div></>
}
Create /components/users02.js (class component)
import React, { Component } from 'react';
//redux
import { connect } from "react-redux";
import { users_list, users_add } from "../redux/actions/usersActions";
class Users02 extends Component
{
async componentDidMount()
{
//this doesn't have to be called since it is being called in the functional component and the state is shared
//this.props.dispatch(users_list());
}
render()
{
//this is made possible be mapStateToProps
const { users } = this.props;
return <>
<h1>Users: 02</h1>
<button onClick={() => this.props.dispatch(users_add())}>Add User</button>
<div className="users">
{users.map((item, index) => (
<h1 key={item.name}>{item.name}</h1>
))}
</div></>
}
}
//these are the state properties you want access to
//these will get added to this.props
const mapStateToProps = state => ({
users: state.users.items
});
export default connect(mapStateToProps)(Users02);
Redux Store Functions
Create /redux/store.js
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers"; //webpack automatically checks index.js
const middleware = [
thunk,
];
const store = createStore(
rootReducer,
applyMiddleware(...middleware)
);
export default store;
Create /redux/actions/types.js
export const USERS_APPEND_ITEM = "USERS_APPEND_ITEM";
export const USERS_LIST_SUCCESS = "USERS_LIST_SUCCESS";
Create /redux/actions/userActions.js
import * as ActionTypes from "./types";
export function users_add()
{
let new_user = {
name: Math.floor(Math.random() * 10000001) + 1, // returns a random integer from 1 to 10000000
};
return {
type: ActionTypes.USERS_APPEND_ITEM,
payload: new_user
}
}
export function users_list()
{
//this async function is made possible by the thunk middleware
//you will often need an async function if you are making an ajax request
return async function (dispatch)
{
let items = [];
//populate your items - normally would be an ajax call
let number_of_users = 10;
for (let x = 0; x < number_of_users; x++)
{
let user = {
name: Math.floor(Math.random() * 10000001) + 1, // returns a random integer from 1 to 10000000
};
items.push(user);
}
//dispatch this action to redux reducers
dispatch(
{
type: ActionTypes.USERS_LIST_SUCCESS,
payload: items
}
);
}
}
Create /redux/reducers/index.js
//combine the reducers
import usersReducer from "./usersReducer";
import {combineReducers} from "redux";
const rootReducer = combineReducers({
users: usersReducer
});
//if not using specific names
// const rootReducer = combineReducers({
// usersReducer
// });
export default rootReducer;
Create /redux/reducers/usersReducer.js
import * as ActionTypes from "../actions/types";
const initialState = {
item: null,
items: [],
loading: false,
error: null
}
const usersReducer = (state = initialState, action) =>
{
switch (action.type)
{
case ActionTypes.USERS_LIST_SUCCESS:
{
var new_items = [];
//merge with previous list???
if (state.items)
{
new_items = [...state.items, ...action.payload];
}
else
{
new_items = action.payload;
}
return {
...state,
loading: false,
items: new_items
};
}
case ActionTypes.USERS_APPEND_ITEM:
{
var new_items = [];
//merge with previous list
if (state.items)
{
new_items = [...state.items, action.payload];
}
else
{
new_items = [action.payload];
}
return {
...state,
items: new_items
};
}
default:
return state;
}
}
export default usersReducer;