React/Redux File Architecture: Ducks It Up!

Talia Marcassa
Building CrowdRiff
Published in
5 min readAug 1, 2017

--

The front end team at CrowdRiff found this awesome article by Alex Moldovan. It outlines a strategy called ‘Ducks’ which lays out a way to organize a React/Redux file system at a large scale.

The basic idea behind Ducks is to create a file structure that is scalable and easy to follow. Using Redux to manage state results in copious numbers of actions that are used in multiple components and a state structure that is several levels deep. As a result, an efficient way of importing functionality into multiple files/components is key to avoiding errors.

The Ducks methodology is also useful for reasoning about what each part of your application is doing. It’s very clear where actions originate and what reducers are dealing with what types.

Ducks also creates a consistent and easy pattern for imports/exports in your file system, eliminating errors and allowing for ease of use across many components.

How To Ducks 🦆

The Ducks file system dictates that the Redux bits (actions/reducers/types) live with the components they are dealing with. You can further this philosophy depending on the needs of your app. Crowdriff also includes any selectors or utility functions in the Ducks folder for that specific component. More general utility functions that are used across the platform, live in their own top-level folder to ensure they are readily accessible.

A selector is a piece of functionality that takes state and returns a new piece of information based on state. It does not handle state changes.

Utilities are helper functions. For instance, Crowdriff has several utility functions that handle re-formatting dates. These are typically more ‘ugly’ pieces of code that clutter up your main components and are more readable if they are relegated to their own file.

The Ducks system also says that imports should happen at folder level, instead of a specific file, let’s explore this further with some examples.

Ducks in Action 🐣

Let’s compare a ‘non-Ducks’ file system to a ‘Ducks’ file system.

In the ‘non-Ducks’ system, if a component needed an action from another file, it would have to navigate to that specific file and import the action. Like so:

import * as actions from 'path/to/actions/modal-actions';

Alternatively, you might have all of your actions living together in one long file. Any time a specific action was needed, you would need to first find the action among the hefty list of actions and then navigate to that specific file to import it.

Using the ‘ducks’ system, you never have to import from a specific file, everything lives in the one source of the truth, the ‘index.js’ in its component’s folder.

Using the Ducks architecture, a ‘Modal’ component would have the following file structure:

📁 modal
-- 📁 ducks
-- 📁 tests
-- 📄 actions.js
-- 📄 reducer.js
-- 📄 types.js
-- 📄 operations.js
-- 📄 index.js
-- 📄 Modal.jsx
-- 📄 Modal.scss
-- 📁 tests

The ducks folder contains the actions, types and reducers that pertain to this particular component, almost as if each individual component is it’s own ‘mini’ app with it’s own bit of Redux helping it run.

The index file (the source of truth) in the Modal folder would import all the actions/reducers/types etc, so that any component could import them from the folder.

We export the reducer as the default from this file because it is the most import piece of functionality leaving this file.

For anyone who needs a quick refresher on importing/exporting in ES6, here’s a super short article on it.

import * as _modalTypes from './ducks/types';
export { _modalTypes as modalTypes }
import * as _modalActions from './ducks/actions';
export { _modalActions as modalActions }
import reducer as modalReducer from './ducks/reducer';
export default modalReducer;

If another component needed to use the ‘closeModal’ action, it would be imported like so:

import { modalActions } from 'path/to/model'
... modalActions.closeModal()

Notice that in the above example you are not importing from a specific file, but just from the root “Modal” folder. This makes the importing process far less painful as you are not searching through a file system for the exact file needed but just pointing to the folder that houses that functionality.

All actions are broken up into Operations and Actions files which separates the sync from the async actions. This creates an easier framework for testing and understanding what each piece of functionality is covering in the app.

There are two test folders, one for the components themselves and a folder living inside the ducks folder that handles all Redux related testing.

Why Ducks?

Besides this adorableness?

The Ducks approach to structuring the Crowdriff App means that our whole App can be more modular because it’s obvious what pieces of Redux are handling what functionality. On a completely practical level, Developers are not having to scroll through masses of files before finding the one they need to work on. If a ‘search action’ is not functioning correctly, it’s very obvious what folder that action or operation lives in.

It would also be fairly straightforward to switch out Redux for another State Management library if the need ever arises, as those files are separated from the React portion of the app.

On a personal note, when I first started at Crowdriff, ‘ducks-ing’ components was a great way to familiarize myself with a large scale App for the first time. It would also be a wonderful opportunity for a team to refactor components as they ‘ducks-ed’ an application.

Keep in mind, Ducks is not a set framework, it’s an idea about how to organize your files and as such can be customized for your specific use case. The key is consistency throughout the app and documentation so that everyone is on the same page. Ducks is a great place to start because someone else has already documented and tested it for you!

Ducks is a useful way to grow a React/Redux app with a file system that grows along with your app. It eliminates the margin for error when importing actions across multiple components and allows for more modularity. Please let us know if you have any thoughts about how to better the Ducks-ing way!

Thank you for reading!

Looking for more? Check out Garret’s article on GraphQL.

Hero image by Roksolana Zasiadko.

--

--