React+Redux Project Structure Best Practices

React+Redux is the most frequently used technology stack in the React ecosystem, but there has always been a variety of voices about how to organize the project structure of React+Redux.This article will discuss the three most common project structures and give personal best practices.

  1. By type

    The type here refers to the type of role a file plays in a project, that is, whether the file is a component, a container, or a reducer, etc. Files with different roles, such as component, container, action, reducer, are placed in different folders, which is the project structure used by the Redux official website example.The structure is as follows:

    actions/
      a.js
      b.js
    components/
      a1.js
      a2.js
      b1.js
    constainers/
      a.js
      b.js
    reducers/
      a.js
      b.js
    index.js
    

    Using this structure to organize your project, whenever you add a new feature, you need to add the components required for this feature under the containers and components folders. You also need to add the actions and reducers used for managing this feature under the actions and reducers folders, respectively. If the action type is placed in another folder, you also need to add them under this folderNew action type file.So when you develop a feature, you need to switch paths frequently and modify different files.This project structure is very inconvenient when the project grows larger.

  2. By function

    A module corresponds to a folder in which the container, component, action, reducer files used by this function are stored.As follows:

    feature1/
      components/
      actions.js
      container.js
      index.js
      reducer.js
    feature2/
      components/
      actions.js
      container.js
      index.js
      reducer.js
    index.js
    rootReducer.js
    

    The benefits of this project structure are obvious: components, states, and behaviors used in a function are all in the same folder, making it easy to develop and extend. Many scaffolds on Github also choose this directory structure, such as https://github.com/react-boilerplate/react-boilerplate .However, there is also a problem with this structure. Redux manages the state of the entire application as a store, and different functional modules can share part of the state in the store (the more complex the project, the more scenarios). So when you dispatch an action in the container of feature1, it is likely to affect the state of feature2 because feature1 and feature2 share partState, will respond to the same action.In this case, functions between different modules are coupled together.

  3. Ducks

    Ducks It's a proposal for a new Redux project structure.It advocates writing associated reducer s, action types, and actions in one file.Essentially, it is based on the status of the application, not on the interface function.This way, the dependencies to manage the same state are all in the same file, and no matter which container component needs to use this state, only the file corresponding to this state needs to be introduced into the component.Such a file (module) is as follows:

    // widget.js
    
    // Actions
    const LOAD   = 'widget/LOAD';
    const CREATE = 'widget/CREATE';
    const UPDATE = 'widget/UPDATE';
    const REMOVE = 'widget/REMOVE';
    
    const initialState = {
      widget: null,
      isLoading: false,
    }
    
    // Reducer
    export default function reducer(state = initialState, action = {}) {
      switch (action.type) {
        LOAD: 
          //...
        CREATE:
          //...
        UPDATE:
          //...
        REMOVE:
          //...
        default: return state;
      }
    }
    
    // Action Creators
    export function loadWidget() {
      return { type: LOAD };
    }
    
    export function createWidget(widget) {
      return { type: CREATE, widget };
    }
    
    export function updateWidget(widget) {
      return { type: UPDATE, widget };
    }
    
    export function removeWidget(widget) {
      return { type: REMOVE, widget };
    }
    

    The overall directory structure is as follows:

    components/  (Application-level generic components)
    containers/  
      feature1/
        components/  (A dedicated component that separates functionality)
        feature1.js  (Container Component)
        index.js     (feature1 Exposed interfaces)
    redux/
      index.js (combineReducers)
      module1.js (reducer, action types, actions creators)
      module2.js (reducer, action types, actions creators)
    index.js
    
    

    In the first two project structures, when container s need to use actions, you can import * as actions from'path/to/actions.js'to bring in all action creators in an action file at once.However, when using the Ducks structure, action creators and reducers are defined in the same file, and import * imports reducers as well (action types are also imported if action types are also export ed).We can solve this problem by defining action creators and action types in a namespace.Modify as follows:

    // widget.js
    
    // Actions
    export const types = {
      const LOAD   : 'widget/LOAD',
      const CREATE : 'widget/CREATE',
      const UPDATE : 'widget/UPDATE',
      const REMOVE : 'widget/REMOVE'
    }
    
    const initialState = {
      widget: null,
      isLoading: false,
    }
    
    // Reducer
    export default function reducer(state = initialState, action = {}) {
      switch (action.type) {
        types.LOAD: 
          //...
        types.CREATE:
          //...
        types.UPDATE:
          //...
        types.REMOVE:
          //...
        default: return state;
      }
    }
    
    // Action Creators
    export actions = {
      loadWidget: function() {
        return { type: types.LOAD };
      },
      createWidget: createWidget(widget) {
        return { type: types.CREATE, widget };
      },
      updateWidget: function(widget) {
        return { type: types.UPDATE, widget };
      },
      removeWidget: function(widget) {
        return { type: types.REMOVE, widget };
      }
    }
    

    In this way, when using actions in container s, we can import {actions} from'path/to/module.js'to avoid introducing extra objects and the tedious task of listing all actions in import.

    Now the Ducks structure is the project structure that is being used in my project. It is still very smooth to use. Welcome to make suggestions for improvement!

Keywords: React github

Added by kelesis on Sat, 01 Jun 2019 19:33:43 +0300