React-redux-starter-kit: Folder structure and naming not clear to me

Created on 30 Jun 2016  路  12Comments  路  Source: davezuko/react-redux-starter-kit

According to the readme 'src/components' is labeled "Reusable Presentational Components", same with 'src/containers'. In the example there are 2 routes: home and counter. It is not clear to me why the HomeView has the suffix "View" and is under 'routes/Home/components' while the counter is named simply "Counter" and is among the reusable components. Why is it split into 2 parts: src/components/Counter and routes/Counter ? I can't see the logic.

Most helpful comment

I think the reason for being inside the routes folder may be due to nesting routes. There is a similar folder structure used in react-router examples: https://github.com/reactjs/react-router/tree/master/examples/huge-apps

Many best practices are not obvious in such a trivial example as a counter. I followed the same organizational structure on my more complicated application with nested routes.

All 12 comments

'src/components' is for Presentational components, not just any component (though I agree the naming of the folder could be improved to something like 'src/presentational').
If you don't see the difference between a Presentational Component and a Container Component, I recommend reading http://redux.js.org/docs/basics/UsageWithReact.html and https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.j9lmjkzhd

For the suffix "View", I have no idea.

For the split between src/components/Counter and routes/Counter:

  • src/components/Counter holds the presentational component (i.e. pure presentation stuff, like the JSX tags).
  • src/routes/Counter holds the Counter container (src/routes/Counter/containers/CounterContainer.js) as well as data manipulation (i.e. logic) and state manipulation (actions and reductions).

"But why is the container inside route?", you may ask. My guess is that you could reuse the same Presentational component (src/components/Counter) in several routes, thus have different Counter containers.
Example:

  • src/components/Counter holds pure presentation of a counter, with buttons
  • src/routes/ForwardCounter holds a container wrapping Counter, and increments (i.e. passes props like the increment function, and the text "Increment" to display on the button)
  • src/routes/BackwardCounter holds a container wrapping Counter, and decrements (i.e. passes props like the decrement function, and the text "Decrement" to display on the button)

I hope I'm not mistaken, and I hope this helps.

I wonder why the component container logic (e.g. CounterContainer.js and modules/counter.js) is in the src/routes/ path and not e.g. in the src/containers folder. The routes folder is now not just the routes configuration but also the components logic. I find that confusing.

I think the reason for being inside the routes folder may be due to nesting routes. There is a similar folder structure used in react-router examples: https://github.com/reactjs/react-router/tree/master/examples/huge-apps

Many best practices are not obvious in such a trivial example as a counter. I followed the same organizational structure on my more complicated application with nested routes.

@frontalicious-martijn I recommend reading about fractal structure this project is using and also huge-apps example @brianzinn posted. The project structure is correct you are just not thinking in fractals yet :smile:

Yes, I'll start with a structure like this, thanx for the feedback guys.

Related to this I have another question. What if I want to reuse my reducer in another route than the one that it's defined at atm. Is this always bad practice and should restructure my routes so it becomes a subroute when I want to use a certain reducer?

Say that I want to use a counter component at the route '/', I cannot do this because the reducer is defined under the counter route.

@jenyckee - I hope I understand your question correctly - all reducers are available everywhere. when you dispatch an action to the 1 store it goes to all reducers. If you need the state from different reducers in another part of your application, i would try looking at mapStateToProps in the container (ie: CounterContainer). In new route definitions using injectReducer to add another reducer is optional. I would watch the egghead videos on redux, if you haven't already to explain those concepts, if they are not familiar. If you go with sub-routes using their own reducer and container you may need something like { React.cloneElement(this.props.children, {prop1, prop2})} in your sublayout to carry props forward. The learning curve is steep on these concepts. good luck.

@jenyckee - I was also facing this problem .. My idea was to create an "src/modules" folder and place the reducers and related stuff there. In my case I had a Notification logic that is used in multiple place so that I put the reducers under "src/modules/Notification/reducers.js". There are "actions.js" and "actionTypes.js" too in that folder. I put only app and/or route specific stuff into "routes/route-name/modules".
"src/modules/ModuleName" reflects reusability and route-independency.

@janoist1 That is exactly what I ended up doing as well. I added a folder src/modules that contains "core" reducers. I'm wondering now if this is against any bad practice since there is no such folder in this project.

@jenyckee there's nothing wrong with that approach, it's how this project was originally structured. However, it doesn't necessarily scale super well, so @justingreenberg introduced the fractal structure to help prevent people from making the same mistakes. You may find this post helpful.

One thing is still unclear to me. The example connects in the countercontainer the counter component to the counter reducer in the counter route. Like this there is only room for one container and hence one component? What if I want to add another component, IncrementButton, that also needs to be connected to the counter reducer. Where do I connect it?

/*  1. ReactRouter -  Create PlainRoute definition object  */
export default (store) => ({
  path: 'counter',

  /*  2. ReactRouter -  Invoked when path match (lazy)  */
  getComponent (nextState, cb) {

    /*  3. Webpack (build) -  Create split point
        4. Webpack (runtime) -  Load async chunk with embedded jsonp client  */
    require.ensure([], (require) => {

      /*  5. Webpack (build) -  Require all bundle contents  */
      const Counter = require('./containers/CounterContainer').default
      const reducer = require('./modules/counter').default

      /*  6. Redux -  Use store and helper to add async reducer  */
      injectReducer(store, { key: 'counter', reducer })

      /*  7. ReactRouter -  Return component */
      cb(null, Counter)

    /*  8. Webpack -  Provide a name for bundle  */
    }, 'counter')
  }
})

@jenyckee I'm playing around with the project structure for a while and it looks nice to me. You've commented on a route component here. The code injects a given reducer to a route component, not a dumb or smart component which should be in a different folder according to the documentation. So if you want to create a then you 1) create a presentational component in the /components/CustomButton folder, create a container in /containers/CustomButton where you connect() your /redux/CustomButton/reducers reducer. And then you can import the container wherever you want. It's not related to any route.
Did I understand your intent correctly?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

marcelloromanelli picture marcelloromanelli  路  5Comments

brandonmikeska picture brandonmikeska  路  4Comments

kolpav picture kolpav  路  4Comments

ciokan picture ciokan  路  4Comments

renanvalentin picture renanvalentin  路  5Comments