Skip to content

⚛️ React-Redux application sample with all the best practices

Notifications You must be signed in to change notification settings

codica2/react-app-best-practice

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

React Redux Sample

Description

React application bootstrapped with Create React App for using with REST API and Redux for state managing.

Both components and redux-specific code (reducers, actions, action types) splitted by feature-first pattern Re-Ducks.

File structure

src/
├── state/        => represents redux
├── views/        => all react components
└── utilities/    => global constants and helper functions

Redux

State folder contains usual store.js and folder ducks, where one 'duck' equals one feature with one reducer. One duck contains actions.js, redurers.js, types.js and optional utils.js.

ducks/
├── duck/
|   ├── actions.js
|   ├── reducers.js
|   ├── types.js
|   ├── utils.js
|   └── index.js
└── index.js

Since index.js of each duck have default export as this feature's reducer, index of ducks folder represents root reducer. So adding a new, changing or deleting existing features in redux being not so painful - all files, related to one feature concentrated in one folder.

It also prevents merge conflicts in situations, when several people working around different features need to touch same files, as types, actions, etc.

// ducks/index.js
export { reducer as form } from "redux-form"

export { default as user } from "./user"
export { default as profile } from "./profile"

/* ... */

// store.js
import * as reducers from "./ducks"

export default createStore(
  combineReducers(reducers),
  reduxDevTools,
  applyMiddleware(...middlewares you use)
)

Index of ducks/ folder = old root reducer with x2 less more code

One more thing about reducers

There is a helper function, called createReducer, used to create reducers, not using basic switch-case template.

const someReducer = createReducer(initialState)({
  [types.YOUR_ACTION_TYPE]: (state, action) => {
    const some_var = "";
    return {
      ...state,
      some_prop: action.payload
    };
  },

  [types.SOME_ANOTHER_TYPE]: (state, { payload: { data } }) => ({
    ...state,
    data,
    loading: false
  }),

  [types.MAY_BE_YOU_WANT_RESET]: (state, action) => ({
    ...initialState
  })
});

Its very useful, for example, if you need to scope out part of reducer to use variables with same name in several case statements.

Tip: switch-case template still can be useful when several types causes same reaction.

About actions

To handle asynchronous actions we usually using redux-thunk middleware and always using action creators.

const someAction = payload => ({
  type: types.SOME_YOUR_TYPE,
  payload
});

const someFetchAction = payload => (dispatch, getState) => {
  dispatch(setLoading(payload.id));

  fetch(GET, `/api_endpoint?some_parameter=${payload.id}`)
    .then(response => {
      if (getState().yourReducer.currentLoading === payload.id) {
        dispatch(setLoaded(response));
      }
    })
    .catch(error => {
      dispatch(setFail(error));
      console.error(error);
    });
};

React

views/
├── routes/       => base router
├── components/   => feature-first components
├── pages/        => layouts, related to routes
├── styled/       => StyledComponents
└── UI/           => reusable components

We splitting components to two parts - Container and Component.

Container file concentrates in itself all logic and HOCs of this feature.

Component itself usually a plain stateless component.

// FeatureContainer.js

import Feature from './Feature.jsx'

const withConnect = connect(...)

const withForm = reduxForm({
  ...
})

const enhance = compose(
  withConnect,
  withForm,
  anyOtherListedHOC
)

export default enhance(Feature)

// Feature.jsx

const Feature = ({props you needed}) => (
  /* some jsx code here */
)

export default Feature

License

react-app-best-practice is Copyright © 2015-2019 Codica. It is released under the MIT License.

About Codica

Codica logo

We love open source software! See our other projects or hire us to design, develop, and grow your product.