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;