Originally published in January 2019, where it reached 30,000 readers. The core idea still holds, and the tooling has caught up: Redux Toolkitās createSlice puts your actions and reducer in one file, which is the ducks pattern built in. Read this post for the why, then reach for Redux Toolkit for the how.
Before I dive deep into the world of React and Redux, allow me to provide some context:
My journey to tech was rather unconventional or non-traditional if you will. I didnāt study Computer Science in college but actually was an English teacher for 7 years before embarking on my journey of learning to code.
During those years in the classroom, I spent a great deal of my time lecturing about structure. And by that I mean:
- the 5 paragraph essay
- the art of writing an outline
- rules of grammar
- & proper sentence construction
Essentially all things structure and organization when it comes to a high school lit class.
This all combined with the fact that Iām also a Virgo means that I like really really like structure. Specifically, organized structure.
At Ada Developers Academy, a tuition-free year-long program that trains women and gender diverse folk to code in Seattle WA, my peers were constantly giving me a hard time over my color-coded notes.
But in my defense, I had just quit my job and had taken this massive risk on learning to code, and this sort of stuff suits me! It brings me peace, joy, and sanity.
And while learning to code, just like my old classroom, I loved when everything had its place.
I love a thoughtfully organized codebase, it genuinely makes me smile.
So it should be no surprise that later on down the road when I became a software engineer at Amazon, I fell really hard for React.
Iām sure a lot of readers may already know, but React has many powerful features including:
- the Virtual DOM
- native compatibility
- a relatively quick learning curve
- helpful developer tools
but most of all:
- the reusability of modularized components
Ever since learning about it, Iāve always been really drawn to the component driven architecture thatās the foundation of React.
TL;DR Reactās modular components are freaking awesome but sometimes state management can get wackadoodle
There are tons of articles/resources out there that provide details on Reactās awesome features so I wonāt dive deep into those here, but I will pause to make sure weāre on the same page when it comes to the modularized and reusable components.
When you build an app with React, you create a bunch of independent, isolated, and reusable components.
You then compose them all together to build complex user interfaces.
And the former grammar obsessed rule following teacher that I am LOVES the encouraged organization and division of the components this implies. I find the reusability of the components to be so helpful for engineers. Plus it always makes it so easy to find things within your code.
To visualize this concept, hereās a screenshot of a React app that aggregates viewing data to create a āMost Binged TV Showsā app:
The app itself can be divided into components:
- the nav
- the show item
- the show list all together
The architecture of that app is pretty straight forward, but as we all know, things can get complicated and tricky and really quickly go from this to this:
And thus developers at this point are forced to discover the sticky or not so glamorous part of React.
Oftentimes, developers want to pass state as props around the app quite a bit. React advocates for a single directional flow and things can get messy when you want to keep your data āin syncā when 2 (or more) components share that same data.
Ideally, the source-of-truth of the data is only in one place. The React docs encourage you to ālift state upā if you have two children that need to access the same data. This means putting the data in the nearest ancestor of the 2 components. But if these two components are very far apart in the tree, the ānearest ancestorā component could be at the TOP LEVEL of the component tree.
And to complicate things even more- the intermediate components may have absolutely NO USAGE for the props itās being passed - it just happens to be stuck in the middle and has to pass it along. No thank you.
My favorite analogy for this concept is the idea of me wanting to tell my cousin a story. But instead of being able to tell her directly that story, I also need to tell my aunt. Now thatās fine if itās a little, non-embarrassing story, but say itās about something I donāt necessarily want my aunt to know about?
Well, bummer dude, I still have to pass it through her regardless.
So the top ancestor has to pass the data down through several intermediate components along the way to get to the proper component, and just like the game telephone we played as kids, this creates a million different opportunities for developer error or unclarity for future users. The original story can get muddled and someone could easily get confused as they try to trace the props as they get passed between the many components.
āaka no bueno-
To view it another way, once state is being passed back and forth and up and down between the component trees, itās easy to imagine that things could get complicated real quick.
And to add insult to injury, my girl Sandi Metz taught me to fear coupling and thatās happening big time between each component and its parent. So to try and move a component around would become super complicated. There is coupling between the component and its parent AND between the componentās children that itās passing props to!
Thus, to no oneās surprise, this impacts performance as every update to the data made will cause ALL of the children to re-render- which can cause massive performance and speed issues!
š¤·š¼āSo itās really a balancing act. š¤·š¼ā
There are many great things that React brings to the table, but as you can see, itās important to find a solution for managing an applicationās state if youāre hoping to build anything more complex than just a to-do list!
When youāre in the designing phase of a project, more often than not, a few things are crucial:
- for the app to be able to scale
- to be able to create something thatās maintainable many months or years into the future
- and dare I say, itād be nice to maintain your sanity when it comes to state management! <a href="https://medium.com/media/1c6bc78161c775edfda8660481d69ec3/href"><a href="https://medium.com/media/1c6bc78161c775edfda8660481d69ec3/href">https://medium.com/media/1c6bc78161c775edfda8660481d69ec3/href</a></a> ### Which brings me to Redux- the state container superhero that saves the day!
So, remember the chaos that was the mess of passing data around the components? That is what Redux successfully helps you make sense of.
Redux is a state management tool for JavaScript applications. Meaning that I can pass data (or the embarrassing story about my most recent Bumble dating escapade) to my cousin without having to also tell my aunt about it as well. Aka, I get to avoid the chaos of that story being bounced between all of the components (or my many crazy uncles), just to update or change one of them.
And this all becomes possible because of Reduxās most important principle: the global store!
So letās talk about that. The big thing to remember is that the entire state of an application is stored in one central location , called the store.
Meaning that each component of your React app gets to have direct access to the state of the application without having to send props down to a child component or using callback functions to send data back up to a parent! And thatās pretty dreamy.
Redux provides a central store that can hold data from ANYWHERE in the application.
To put it differently, Redux completely eliminates this messy tunneling that can happen when youāre passing data from a parent down to many different subcomponents and manipulating it in all those places.
Now, this is a diagram of how that all happens:
And Iāll show how to add each piece of it to a React app to illustrate how it all works together.
So for the app I showed earlier, you can imagine that each color here is a different React component.
And currently with just React, the data in this app flows directionally as so:
BUT I want to add redux so that the state and data transfer looks more like this:
And to make that happen, the first thing to do is to build the store.
HOW TO ADD REDUX TO YOUR REACT APP
STORE
Itās crucial to remember that the store in Redux is like the human brain. Itās absolutely fundamental: the state of the whole application lives inside the store. So to start, you should create a store for wrapping up the state.
Create a directory for redux within your src folder:
src/redux/
Create a store folder within that:
src/redux/store/
Then create a new file named index.jsand finally initialize the store:
// src/redux/store/index.js
import { createStore } from āreduxā;
import rootReducer from ā../reducers/indexā;
const store = createStore(rootReducer);
export default store;
The createStore()is the function for creating the Redux store and while you may also pass an initial state to createStore(), most of the times you donāt have to. Although passing an initial state can be useful for server-side rendering, traditionally, the state comes from reducers , which is what I did here- it takes a reducer (rootReducer) as the first argument.
BUT WAIT. I havenāt really explained what reducers do yet!
Remember, the state comes from the reducer . What matters now is understanding what reducers do.
REDUCERS
Letās go back to our diagram.
In Redux, reducers produce the state. The state is not something you create by hand.
Reducers specify how the applicationās state changes. One of the principles of Redux is that the state is immutable and cannot change in place.
In plain React, the local state changes in place with the function setState().
But in Redux, you cannot do that.
A reducer is just a Javascript function- it takes two parameters: the current state and an action, which is why the reducer must be pure- meaning that it returns the exact same output for the given input.
And creating a reducer is actually pretty simple.
Hereās how:
Create a directory for the root reducer within redux:
src/redux/reducers/
Create a new index.js file within that folder.
In that file:
// src/redux/reducers/index.js
const initialState = {
shows: []
};
const rootReducer = (state = intitialState, action) => state;
export default rootReducer;
This reducer is sort of a silly one because it returns the initial state without doing anything else.
But definitely notice how the initial state is passed as a default parameter.
Now, reducers are without a doubt the most important concept in Redux. Let me say it again, Reducers produce the state of the application.
But this should then beg the question, how does a reducer know when to produce the next state?
ACTIONS
Well, thatās where actions come in!
One of the principles of Redux is that the only way you can change the state is by sending a signal to the store. This signal is an action.
āDispatching an actionā is the process of sending out a signal.
And, how do you change an immutable state? Well, you donāt. The resulting state is a copy of the current state plus the new data.
And you may be thinking, woah Lauren, thatās a lot of data to know.
But the reassuring thing is that Redux actions are nothing more than Javascript objects! This is an example of what it might look like:
{
type: 'ADD_SHOW';
payload: { title: 'Marvelous Mrs. Maisel', rank: 1, picture: 'https://image.jpg' }
}
So letās build the action
Create a directory for the actions:
src/redux/actions/
Create a new index.jsfile within that folder
In that file:
// src/redux/actions/index.js
export const addShow = show => ({ type: āADD_SHOWā, payload: show });
Every action requires a type property for describing how the state should change (and itās really just a string). The reducer will use that string to determine how to calculate the next state. And you can specify a payload as I did if you like.
Referring back to the diagram and because types are just strings and strings are prone to typos and duplicates, itās better to have action types declared as constants.
ACTION TYPES
Itās a best practice to wrap every action within a function which definitely helps to avoid errors that would be difficult to debug.
So letās also build a simple action-creator.
Create a directory for the constants within redux:
src/redux/constants/
Create a new action-types.js file within that folder.
In that file:
// src/redux/constants/action-types.js
export const ADD_SHOW = āADD_SHOWā;
Then open upsrc/redux/actions/index.jsand update the action to use action types:
// src/redux/actions/index.js
import { ADD_SHOW } from ā../constants/action-typesā;
export const addShow = show => ({ type: ADD_SHOW, payload: show });
Hold up, let me check in. Readers, are you still with me??
No? Okay maybe it would help, before going any further, if I recap the main Redux concepts and everything that just happened:
- the Redux store is in charge of orchestrating all the moving parts
- all of the state lives as a single, immutable object
- as soon as the store receives an action, it triggers a reducer
- the reducer returns the next state
The order sort of goes like this:
- An action first occurs within a component, such as someone trying to add a show, filter the view, see the details of a component, or even deleting a component, etc.
- That then calls the action and action type
- Which then grabs the particular reducer that will update or modify the state
- And once the state is changed, the view is rerendered
Boom! And there you have it! That is how Redux works!
Congrats! You now know everything there is to know about how to add Redux to a simple app and should feel ready to scale it to an app on your own.
Except, wait, you may have noticed that while I was setting up just the beginnings of the Redux architecture, I had to create a bunch of additional folders and files.
Now imagine adding in ALL of the functionality of a traditional app. If I was to continue down this path, itād be easy to imagine how things could get pretty messy and/or confusing real quick.
Youād have to edit the constants in one file, then edit the reducer in another, then edit the actions in another, and finally, edit the action creators in yet another.
Itās like a boring tennis match no one cares to watch!
And it means that even adding just a small feature might equate to editing and adding several different files! Which is kind of a headache.
<a href="https://medium.com/media/dc720bab7c93f8e93aa412f88473258b/href">https://medium.com/media/dc720bab7c93f8e93aa412f88473258b/href</a>This way of organizing your Redux is the most common and is essentiallyBy Type.
But as was just demonstrated, you end up hopping back and forth between files for a single piece of related functionality because the constants, actions, and action-creators are imported into the reducer file and the action-creators are also imported into the container to be dispatched. And all of this becomes a little annoying, wouldnāt you agree?
What I found to be really wild was that there is actually no prescribed one way of organizing your Redux files. ThisBy Typemethod is super common and is taught in a ton of tutorials, but obviously, itās sorta flawed.
My theory is that often times when youāre learning about Redux and the roles of actions and reducers, you start off with really simple examples. And most tutorials available donāt take you to the next level. But if youāre building something with Redux thatās more complicated than a todo list, you realize pretty quickly that you may need a smarter way of scaling your codebase over time.
These actions, constants, and reducers are all related but are in a fractured state and honestly, switching from one file to the next not only takes a few seconds, but can make your head a little fuzzy, and I feel like it really slows down my production efficiency.
Simply put, itās hard to maintain! š š¼
So obviously I felt frustrated by this when I was first learning React and absentmindedly set up an appās architecture in this manner.
All of my English teacher organizational habits came rushing back to me. I KNEW that there was a way to strategic and thoughtful with the organization of a codebaseās architecture.
<a href="https://medium.com/media/e1beaf0e8d38fc358810b9351f3bfc9c/href">https://medium.com/media/e1beaf0e8d38fc358810b9351f3bfc9c/href</a>I dove into the research and Medium articles knowing that there had to be something out there that would offer me some piece of mind. And I was right, there were lots of ideas out there about it.
Some people were suggesting online that a solution to this frustration might be to organize your code by feature. As in:
- encapsulating a component (the container)
- its state (the store)
- and its behavior (the actions) into a single folder
All following the Reactās component concept.
But this means that youād have to tie a slice of the Redux store to a container and that is completely counterintuitive to the core of what Redux promotes!
So Iām not about to advocate for this pattern to you.
And so back to the drawing board I went and further research led me to ultimately discoveringā¦
The moment youāve all been waiting for, based on the title of this article, Ducks to the rescue!
<a href="https://medium.com/media/ee81640b082601e024ce543161506c53/href">https://medium.com/media/ee81640b082601e024ce543161506c53/href</a>But what are Ducks?
Well, Erik Rasmussen, who coined the term, realized that he was often creating and editing one piece of functionality at a time.
He kept needing to add {actionTypes, actions, reducer}tuples for each use case. Just as I was, he was keeping these in separate files and separate folders, however, 95% of the time, it was only one reducer/actionpair that ever needed their associated actions.
Thus, it made more sense for these pieces to be bundled together in an isolated module thatās self-contained and can even be packaged easily into a library.
Ducks is essentially a proposal for bundling reducers, action types, and actions all into the same file.
But WHY specifically would this organization structure be the solution to this architectural problem?
Or rather, why get excited about Ducks?
Beyond their adorable-ness factor that is š
<a href="https://medium.com/media/b5a7a41b4c13f2d812b7f584a05c30d8/href">https://medium.com/media/b5a7a41b4c13f2d812b7f584a05c30d8/href</a>Well, Ducks seeks to solve the issue of the toggled back and forth repetitive nature of organizing by type. With Ducks, rather than splitting up all related code, I can package it into redux modules!
So letās refactor the React app you just created and convert our Redux to DUCKS.
Remember that the earlier example created the functionality of adding a show, and now youāll need to move the actionTypes, actions,andreducers all into just one file!
HOW TO CONVERT YOUR REDUX TO DUCKS
- Create a directory for the Ducks within redux:
src/redux/ducks/
Create a newindex.js file within that folder:
Grab the actionTypes from the constants folder and put it into the new ducksfile:
// src/redux/ducks/index.js
export const actionTypes = {
addShow: {
ADD_SHOW = āADD_SHOWā
}
};
Let the deleting begin and delete the entire constants folder!
Next up is actions file. Grab the actions:
// src/redux/actions/index.js
addShow = show => ({ type: āADD_SHOWā, payload: show });
- And put that into the ducks file:
// src/redux/ducks/index.js
...
export const actions = {
addShow = show => ({type: ADD_SHOW, payload: show});
};
Continue the fun and delete the entire actions folder!
Lastly, tackle the reducers.
First grab this reducer:
// src/redux/reducers/index.js
const initialState = {
shows: []
};
And actually, letās change the reducer because the one from the first example does nothing other than returning the initial state, which is easy to fix by making it a switch statement.
In the ducks file add:
// src/redux/ducks/index.js
...
export const rootReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_SHOW:
return { ā¦state, shows: [ā¦state.shows, action.payload] };
default:
return state;
}
} **;**
- And of course, donāt forget to delete the reducer folder!
13. TA-DA! Youāve just created a modularized Ducks file!
This file now officially contains ALL of the functionality that was built out before. It runs exactly the same way, but now, it is modularized into a clean, comprehensible file, or duck , for optimized state management.
Just to really bring things home, remember when you created all of those folders and all of those files that you separated and fragmented from one another?
Well, compare that mess to this new file, which contains all of that same capability in a legible fashion, and Iāll call that a win!
Plus, itās pretty easy and painless to create.
Thus, the art of ducks-ing is a way of structuring an app to become more modular, which yes, is a real sexy buzz word in the world of software engineering, but itās really great because it now has become super obvious which piece of Redux is handling which functionality.
On a completely practical level, you no longer have to scroll through masses of files before finding the one you need to work on.
But this is only one way of structuring redux. There are plenty of other options.
And the simple fact that Iām encouraging readers to explore ways to creatively define what might work best for you may, in fact, mean that the structure-obsessed type-A person that I once was is leveling up to become someone who embraces ambiguity and discovers that sometimes you can define your own rules and doing whatever is best for your code and your team is ultimately the right choice!
What I love most about Ducks is how clean and clear it is.
It removes a lot of unnecessary boilerplate and you can easily add a simple action or even an appās entire functionality by changing a single file.
Iāve been on teams that have adopted this structure and we were really happy with it.
Maybe you will be too?!
Give it a shot and let me know what you think.
Hit me up on Twitter- @LoLoCoding and tell me all about your own quacky architectural solutions!
And be sure to check out Rasmussenās original proposal to learn more too!
Also, if youāre a visual/audio learner, hereās a video of me presenting this information at CascadiaJS in 2018.
Follow my journey: š©š¼āš»@LoLoCoding
This post started a good discussion when it first ran. Read the original responses on Medium. If youāve moved from hand-written ducks to Redux Toolkit, share in the comments what changed for you.





















Top comments (0)