Skip to content

Instantly share code, notes, and snippets.

@abhinavmsra
Last active July 8, 2020 17:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save abhinavmsra/0391e981757f9cc2b2cb9331b5c8f706 to your computer and use it in GitHub Desktop.
Save abhinavmsra/0391e981757f9cc2b2cb9331b5c8f706 to your computer and use it in GitHub Desktop.

Project boilerplate

While React itself doesn't enforce any specific project strucutre or setup for that matter, it's always better to have a boilerplate at disposal to kickstart a React project.

After studying some of the available boilerplates, we found react-slingshot from a Pluralsight Author coryhouse quite handy. However it is to be noted that it's just a recommendation and subject to personal preference and at some times project requirements.

Project Strucutre

After working on multiple React projects here at Nimble, we settled down on some generic code organization and stucture that we want to lay down here.

  1. actions/: All the Redux actions creators go under the actions directory. It's highly advised to organize the action creators based on the screens. We will discuss about it in details in next section.

  2. adapters/: Organize the code making network and/or async tasks under this directory.

  3. components/: This directory hosts all the dumb React components. They receive props from React components under screens and render themselves and their children accordingly. These components should have no direct association to Redux. Redux actions must instead be passed down from the screens.

  4. config/: Holds the app specific configuration variables. As always, do not store any secret keys here.

  5. constants/: Define all the actions types under this directory. Ideally, the organization of this directory should match that of the actions.

  6. containers/: Holds the top level React components. These React components are mounted by the react-router and map the Redux actions and state to props and pass down to the screens. Refrain youself from having any logic inside these components.

  7. lib/: All the custom libraries that stand on their own and can work under any Javascript ecosystem go inside this directory.

  8. reducers/: Holds the Redux reducers. Ideally it should reflect the folder structure of actions and constants.

  9. screens/: Holds the page specific components which then renders all other dumb components to build any specific page. These components map one-to-one to those under containers.

  10. services/: Holds the Service Objects used inside the project. By definition, these classes should encapsulate one single process of the app.

  11. store/: Redux store objects.

  12. utils/: Any utilities used in the project should be placed inside this folder.

  13. App.jsx: The parent React component of the app. Bridges the react-router and Redux store into the app flow.

  14. routes.js: All the app routes are defined in this file.

Guidelines on Redux Store Management

Here at Nimble, we employed three different strategies to have a organized and maintainable app store.

  1. Entity Based Store Strucutre: Initially, we had the instinct that organizing store based on the entity types would be most efficient as store is indeed the database of the frontend app. The store would look like this:
users: 
  isFetching:
  fetchSuccess:
  fetchFailure:
  users: []
articles:
  isFetching:
  fetchSuccess:
  fetchFailure:
  articles: []
comments:
  ...

The store is certainly most elegant. But we stumbled across few issues: * filtering specific entites from a global store and then passing them down to components per page addded quite a lot of overhead. * although the store was concise, handling of data and refreshing specific entities was quite a nightmare.

We abandoned this approach.

  1. API Based Store Structure: Our next attempt was to organize the store based on the API response. The concept was quite straightforward -- a page makes a request for data, so lets simply organize the store around the API responses.

The store schema looked like this

articles: 
  isFetching:
  fetchSuccess:
  fetchFailure:
  articles: []
  users: []
  comments: []
  ...

This appraoch had the advantange that now fewer sub-stores needed to be passed down to the subsequent components and the
associated entities we encapsulated together, but we still had the issues of * filtering the global store. * now an entity of one type could be under multiple substores, so updating a single entity required dispatching multiple actions. * when used with REST API we eventually ended up with a store schema similar to entity based with added overhead of handling the relationships.

At last, we had to abandon this approach too.

  1. Screen Based Store Strucutre: We eventually decided to employ a screen based store schema where store is designed based on the requirements of a page. So basically if a page named dummy displays lists of say entity of types A, B and C, the corresponding store schema would be

    dummy:
      A:
        ...
      B:
        ...
      C:
        ...
    

    If any other pages need similar entities, a replica of the store schema would be nested under the page name in the store.

    dummy:
      A:
        ...
      B:
        ...
      C:
        ...
    dummyAgain:
      A:
        ...
      D:
        ...
    

    We find the screen based schema quite handy, as

    • there is no overhead of filtering entites from a global store and passing down to the components.
    • clearing of store and refershing a specific entity is quite simple.
    • It allows for caching store on a page level which plays quite well for offline supports.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment