Hi everyone,
There have been a few changes in the recent years since we last discussed our folder structure conventions: Suggestion: Refactor the react_app folder structure
and maybe it’s time to adjust our conventions.
as mentioned in Code Structure | Redux,
there are a few common patterns that most Redux developers tend to use,
let’s start with the first one - Rails-style: separate folders for “actions”, “constants”, “reducers”, “containers”, and “components”**
I think it was inconvenient for developers to do all of the imports from the different directories,
and it basically led us after the discussion in Suggestion: Refactor the react_app folder structure to move into no. 2 on that list, “Feature folders” / “Domain”-style: separate folders per feature or domain, possibly with sub-folders per file type,
meaning most of our folders today look like this ( the example is with redux’s connect HOC style):
Component/
// main file to export the redux-connected component and reducers
index.js
// the React component containing the JSX, life-cycle methods, and some logic.
Component.js
/** The Redux actions that are passed from the index.js into the connect HOC to the Component.js.
* Since we started to use the API middleware, we see that often this file isn't needed
* and some of the logic has moved into the Component.js file using React state.
*/
ComponentActions.js
/** same as the above, since using the API middleware, it's often not in use, the logic moved into
* Component.js inner state. It is needed in cases the component has more data than what the API
* returned to pass to other components.
*/
ComponentReducer.js
/** selectors that are used in the index.js file to select attributes from Redux state and pass them to t
* the component can be re-used from other components.
*/
ComponentSelectors.js
// usually storing here the Actions types so it would be easier to re-use in reducers files.
ComponentConstants.js
// more logic that has moved from component.js to make the component.js file easier to read and test.
ComponentHelpers.js
/** in the tests directory we write tests for each exported function from the above files.
* usually, it gets up to 5 different files
*/
__tests__/
Redux-toolkit
Redux recommends the usage of “feature folders”, with all the Redux logic for a given feature in a single “slice/ducks” file".
they created a library that reduces the boilerplate of many files,
for example, the usage of createSlice: createSlice | Redux Toolkit
internally it uses their createAction
and createReducer
tying together Reducers, Actions and constants types in one file, meaning that we will combine 3 files in one.
Hooks
I think that the biggest change the React-Redux community had faced is the move to Hooks,
Hooks provide you the ability to write function components instead of class components by exposing the libraries’ inner context, they are way easier to write and I guess that’s why the community loves it so much.
examples of hooks:
// React
useState // for managing state
useEffect // lets you perform side effects in function components
// React-Redux
useSelector // Allows you to extract data from the Redux store state, using a selector function.
useDispatch // This hook returns a reference to the dispatch function from the Redux store. You may use it to dispatch actions as needed.
useStore // This hook returns a reference to the same Redux store that was passed into the <Provider> component.
this drastic change means that we move more logic into the Component.js file,
and no need to use the connect
wrapper in the index.js file,
although the connect
function might have better performance, Redux themselves recommend using hooks. see: Hooks | React Redux
I think our folders could look simpler, e.g:
Component/
// export point for the all directory for easier imports. contain also the JSX, life-cycle methods, and some logic.
index.js
// Redux reducer logic and associated actions, I would write also the selectors and constants here if needed.
componentSlice.js
__tests__/
Writing tests
The final thing I would like to discuss is our tests conventions,
it makes it so hard to write 5/6 tests per component, even for a simple one,
The React community had moved almost entirely to use React Testing Library | Testing Library
The React Testing Library is a very light-weight solution for testing React components. It provides light utility functions on top of react-dom and react-dom/test-utils, in a way that encourages better testing practices. Its primary guiding principle is:
The more your tests resemble the way your software is used, the more confidence they can give you.
This approach doesn’t do tests-snapshots, which is the no. 1 factor to prevent a PR to get merged in time, because the PR becomes so big!
I believe we should move away from snapshotting into a more integration-tests approach.
Redux follows the same spirit: Writing Tests | Redux
and mention that Redux code can be treated as an implementation detail of the app, without requiring explicit tests for the Redux code in many circumstances.
this way we will also get rid of a few more unit test files and the tests will be more beneficial.
Waiting to hear your thoughts about it,
Thanks, Ron