The first thing is to install libraries for redux, as you may guess:

1
npm install -S redux react-redux redux-logger reselect redux-persist

redux-logger is a redux middleware which is really really helpful in development, but it’s up to you whether to use it or not. reselect is a memoization implementation for redux. redux-persist is for leveraging localStorage/sessionStorage browser APIs in redux.

Assuming that you have the react project structured in your own way, I’d create a new folder named redux inside the src/app folder:

1
mkdir -p src/app/redux

For demonstration purposes, we gonna setup user reducer, which is specific to user domain.

Create a root reducer and store first, though.

1
2
# inside the 'redux' folder:
touch root-reducer.js store.js

Then create user reducer-related files:

1
2
3
4
# inside the 'redux' folder:
mkdir -p user
cd user
touch user.reducer.js user.types.js user.actions.js user.selectors.js

Overwhelming already? Nah~ it’s not, let me explain what the files do.

user.reducer.js is a file that contains real reducer, which can be called CPU of redux. user.types.js will be containing action types as variables(I’d call it mapping), which is a good convention in redux world. user.actions.js has real action definitions in it. user.selectors.js is for memoization which is a great concept for pure functions, and it’s all about performance.

Here is our root-reducer.js file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import { combineReducers } from 'redux';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';

import userReducer from './user/user.reducer';
import cartReducer from './cart/cart.reducer'; // just ignore this, it's only for demonstration of redux-persist setup

const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['cart']
};

const rootReducer = combineReducers({
  user: userReducer,
  cart: cartReducer,
});

export default persistReducer(persistConfig, rootReducer);

Here is our store.js file then:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import { createStore, applyMiddleware } from 'redux';
import { persistStore } from 'redux-persist';
import logger from 'redux-logger';

import rootReducer from './root-reducer';

const middlewares = [];

if (process.env.NODE_ENV === 'development') {
  middlewares.push(logger);
}

const store = createStore(rootReducer, applyMiddleware(...middlewares));

const persistor = persistStore(store);

export { store, persistor };

user.types.js file:

1
2
3
export const UserActionTypes = {
  SET_CURRENT_USER: 'SET_CURRENT_USER'
}

user.reducer.js file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import { UserActionTypes } from './user.types';

const INITIAL_STATE = {
  currentUser: null
}

const userReducer = (state=INITIAL_STATE, action) => {
  switch (action.type) {
    case UserActionTypes.SET_CURRENT_USER:
      return {
        ...state,
        currentUser: action.payload
      }
    default:
      return state;
  }
}

export default userReducer;

user.actions.js file:

1
2
3
4
5
6
import { UserActionTypes } from './user.types';

export const setCurrentUser = user => ({
  type: UserActionTypes.SET_CURRENT_USER,
  payload: user
})

user.selectors.js file:

1
2
3
4
5
6
7
8
import { createSelector } from 'reselect';

const selectUser = state => state.user;

export const selectCurrentUser = createSelector(
  [selectUser],
  (user) => user.currentUser
)

Phew ~ it’s time to set it up in our index.js file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';

import './index.scss';
import App from './App';
import { store, persistor } from './redux/store';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <BrowserRouter>
        <PersistGate persistor={persistor}>
          <App />
        </PersistGate>
      </BrowserRouter>
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

Let’s use our redux store in any component of our application finally:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';

import { setCurrentUser } from './redux/user/user.actions';
import { selectCurrentUser } from './redux/user/user.selectors';

const YourComponent = props => {
  const { currentUser, setCurrentUser } = props;

  //Do something fantastic with the state and the dispatch!

  return (
    <div />
  );
}

const mapStateToProps = createStructuredSelector({
  currentUser: selectCurrentUser
});

const mapDispatchToProps = dispatch => ({
  setCurrentUser: user => dispatch(setCurrentUser(user))
});

export default connect(mapStateToProps, mapDispatchToProps)(YourComponent);

That’s it. Once you setup the redux, it’s pretty much repetitive task to use it in every corner of your application.

Happy coding! 😎