Hello, everyone.
My name is Lauren Lee and my pronouns are she and her.
My talk is called why I decided to modularize my ducks in the React app.
We will talk about good and bad of React, explore some of the problems within Redux
and I will introduce and propose one possible solution.
That solution is called ducks.
My journey to tech was rather unconventional or non-traditional, if you will.
I didn't study CS in college but instead was an English teacher for seven years before
embarking on my journey learning to code.
I spent a lot of time in the classroom lecturing on structure and I mean the five paragraph
essay, the art of writing an outline, rules of grammar and proper sentence construction.
Essentially, all things structure when it comes to a high school lit class.
This combined with the fact I am a Virgo means I really like structure, specifically organized
structure.
At Ada developer's academy, a program that trains folks to learn to code here in Seattle
my peers were giving me a hard time over my color-coded notes.
In my defense, I just quit my job.
I had taken this massive risk of learning to code and this sort of stuff stock markets
me and brings me peace, joy and sanity.
While learning to code, just like my old classroom, I loved when everything had its place.
I love a thoughtful organized codebase.
It genuinely makes me smile.
Later on down the road when I became a software engineer at Amazon I fell really hard for
React.
As I am sure many of us know it has powerful features including the virtual DOM, relative
quick learning curve, helper developer tools and the reusability of modularized components.
I have been drawn to the components of the architecture that is the foundation of React.
I am sure a lot of us are familiar with React so I won't bore by you explaining the intrinsic
characteristics.
But I will pause for the moment.
When you build with React, you create independent and isolated reusable components and compose
them together to build complex interfaces.
The former grammar obsessed rule following teachers I am loves the encouraged organization
and division of components this implies.
Finding the reusability of -- I find the reusability of the components to be so helpful for engineers
plus it always makes it easy to find everything within your code.
Today, I am going to be using a React app that aggregates viewing data to create a most
binged TV show app.
This app can be divided the nav, the individual show, and the list view.
Now, the architecture of this app is relatively straightforward, but we all know that things
can get complicated really quick.
Thus developers at this points are often forced to discover the not-so-glamorous parts of
React.
Oftentimes we will want to pass data as props around the app quite a bit and React advocates
for a single directional flow and things get messy when we want data in sync when two or
more components share that data.
The source of truth is only in one place.
React docs encourages us to lift state up.
If you have two children that need to access the same data and this means putting the data
in the nearest ancestor of the two components.
If these two components are very far apart in the tree, the nearest ancestor could be
at the top level of the component tree.
To complicate things even more, the immediate components, have absolutely -- may have absolutely
no use for the props it is being passed.
They just happen to be stuck in the middle and have to pass it along.
No, thank you.
My favorite analogy for this is the idea of me wanting to tell, say, my cousin, a story
but instead of being able to tell her directly that story I need to tell my aunt.
That is fine if it is, you know, a little non embarrassing story but say it is about
something I don't necessarily want to tell my aunt about bummer.
I still have to pass it through her regardless.
The top ancestor has to pass data down to several intermediary components along the
way to get to the property component and just like the game telephone this creates opportunities
for errors.
The original story can be muddled and some can get confused as they try to trace the
props that get passed between the many components.
To view it another way, one state is being passed up and down and in between the component
tree, and it is easy to imagine how things could get complicated and to add insult to
injury, my girl Sandy Mets taught me to fear coupling and that is happening big time between
components and parents to try to move a parent around would be complicated.
There is coupling between the components and its parents and between the component's children
that it is passing props to.
Thus, to no one surprise, this impacts performance as every update made to the data causes all
of the children to rerender which causes massive performance and speed issues.
It is really a balancing act.
There are many great things React drink brings to the table.
It is important to find a solution for managing an application state if you are looking to
build something more complex than a to-do list.
When we are in the designing phrase of a project, we often, more often than not we want an app
to be able to scale.
We want to be able to create something maintainable for many months or years into the future and
dare I say it be nice to maintain our sanity when it comes to state management which brings
me to Redux.
The state container superhero that saves our day.
Remember the chaos that was the mess of passing data around our components?
That is what Redux successfully helps you make sense of.
Redux is a state management tool for JavaScript applications meaning I can pass data, or say
the embarrassing story about my most recent Bumble escapades to my cousin without telling
my aunt about it.
AKA I get to avoid the chaos of that story being bounced around all the components or
my many crazy uncles just to update or change one of them and this is important because
of Redux's most important principle: The global store.
Let's talk about that.
The big thing to remember is that the entire state of the 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 child components or using callback functions to send data back up to a parent.
That is pretty dreamy.
Redux provides essential storage that can hold data from anywhere in the application.
To put it differently, Redux completely eliminates the messy tunneling when you are passing data
down from a parent to subcomponents and manipulating that.
Today I am going to add each piece of Redux to my React app to show how that all works
together.
So for the app I showed you earlier, you can imagine that each color is a different React
component.
Currently, with just React the data in this app flows directionally as so.
I want to add Redux so that the data transfer and state look more like this referring back
to the Redux diagram had first thing we have to build is our store.
It is crucial to remember the store in Redux is like the human brain and absolutely fundamental.
The state of the whole app lives inside the store.
To start playing with Redux we should create a store for wrapping up the state.
Shall we all together?
OK.
To get started, we will create a folder and call it Redux.
Within Redux we will then create a folder for the store.
Within the store, we will create a file and call it index.js.
Copy some code and put in there.
The create store function I have here is the function for creating the Redux store.
You may pass initial state to create store you don't have to most of the time although
it can be useful for service side rendering, traditionally the state comes from the reducers
which is what I will do here as it takes the reducer as the first argument, root reducer
in our case.
Wait.
I haven't explained what reducers do yet.
I said before that state comes from the reducer what matters now is understanding what reducers
do.
Let's go back to our diagram.
In Redux, the reducers produce the state and the state is not something you create by hand.
Reducers specify how the application state changes.
One of the principles of Redux is that the state is immutable and cannot change in place.
In play and React the local state changes in place with function setstate.
In Redux, you can't 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.
Creating a reducer is actually pretty simple.
So let's do that all together.
I will pick up where I left off and create another folder and call it reducers.
Within reducers a file index.js.
Pop in this code.
Great.
The reducer here is sort of a silly one.
Oh, I am not on the slide.
This reducer here is sort of a silly one in that it returns the initial state without
doing anything else but definitely notice how the initial state is passed as the default
parameter.
OK.
We have seen that?
Good.
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?
Well, that is 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, the signal is an action and how do you change immutable state?
You don't.
Resulting state is a copy of the current state plus the new data.
You may be thinking whoa, Lauren, that is a lot of data to know, but the reassuring
thing is that Redux actions are nothing more than just JavaScript objects.
This is an example of what one might even look like.
OK.
You know the drill.
Let's create a simple action all together.
Again, we will create a folder.
Actions.
File index.js and prop -- plop in it add show here.
Today I think it will be fun to add the functionality of adding a show.
Every action requires a type property for describing how the state should change and
it is really just a string.
The reducer uses that string to determine how to calculate the next state and you can
specify a payload, just as I did, if you would like.
In my example, the payload is a new show.
OK.
Back to the diagram.
Since types are just strings and strings are as we know prone to typos and duplicates it
is better to have action types declared as constants.
It is best practice to wrap every action within a function which helps to avoid errors.
Let's also do that quickly and create a simple action creator all together.
Again, constants within constants, a file, and quite simple there.
Next up, I want to open up my actions folder and I am going to now use what we just created.
I will remove the quotes and of course I will have to import it from a local directory.
From, I think, I called it constants/action-types.
Great.
Now we are using that.
You all still with me?
No?
OK.
If not, I think it would be important to take the time before we go any further to just
quickly recap the main Redux concepts we just implemented.
The Redux store is in charge of orchestrating all of the moving parts.
All of the state lives in a single immutable object and as soon as the store receives an
action it triggers as a reducer and the reducer then returns to next state.
The order sort of goes like this, an action first occurs within a component, say someone
trying to add a show, or maybe filter the view, see the details or delete a component
even, then that calls the action into action type which grabs the particular reducer that
will update and modify the state, and once that state is changed, the view is rerendered
and that is how Redux works.
Congrats.
You now know everything there is to know about how to add Redux to a React app.
You should feel ready to scale it to an app on your own except... you may have noticed
that while we were setting up our basic, very simple beginning other Redux app, we had to
create a bunch of folders and files.
OK.
Imagine this.
You decide to add all of the functionality a traditional app has beyond just adding a
show maybe you want to filter and edit and delete it.
Whatever it is.
If we continue down this path, it would be easy to imagine how things could be messy
or complicated or confusing quickly because you will then have to edit the constants in
one file, the reducers in another, the action creators in another, and yet again go back
and edit the actions so you are pretty much playing tennis, emotionally, physically, everything,
match over here because just to add one single, small piece of feature functionality equates
to editing and adding several different files.
This kind of just gives me a headache.
This is a way of organizing your Redux by type and that is essentially the most common
but you end up jumping back and forth for files related to a single piece of functionality
because the constants action creators are imported to the reducer file and the action
creator are imported into the container to be dispatched and all that 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
our Redux files.
This by-type method, yes, is super common, and it is taught in a ton of tutorials but
it is obviously sort of flawed.
My theory is oftentimes when you are first learning about Redux and the roles of actions
and reducers you start off with a really simple example.
Most tutorials don't take you to the next level but if you are building something with
React and Redux that is more complicated than a to-do list you realize quickly you may need
a smarter way of scaling your codebase over time.
These actions, constants and reducer are all related but exist in the fractured state and
switching back and forth from file to file only takes a few seconds.
I am going to give you that but it can make my head fuzzy and simply put I find it hard
to maintain.
Thus, yet again, all my English teacher organizational habits came rushing back to me and I knew
there had to be a way to be strategic and thoughtful with the organization of my codebases
architecture.
I dug into the research on Medium articles knowing there must be something out there
that would offer peace of mind and I was right.
There are a lot of ideas out there about it.
Some people suggested online a solution to the frustration might be to organize your
code by feature as encapsulating the component and store into a single folder following the
React component concept.
This means you would have to tie a slice of the Redux store to a container and that is
counterintuitive to the core of what Redux promotes.
Bangladesh to the drawing board I went and further research led me to discover the moment
you have been waiting for based on the title of this talk ducks to the rescue.
What are ducks?
Erik Rasmussen kept needing to add tuples and such for each use case and was keeping
them in separate files and folders, however, 95% of the time, it is only one reducer and
action pair that ever needs their associated action thus it makes more sense for all the
pieces to be bundled into isolated modules that can self-contained and packaged easily
into a library.
Ducks is a proposal for bundling reducers, action types and actions into the same file.
Why would this be the solution to our problem or why get excited about ducks?
Beyond their adorableness factor.
Ducks seek to solve the issue of the toggled back and forth repeated feature of splitting
up.
We can repackage it into the Redux modules.
Let's get to the fun part and convert or Redux to ducks, shall we?
Great.
Rhetorical, yes, I suppose.
Remember, our example was trying to create the functionality of adding a show and together
right now we are going to move the action types, actions and reducers all into one file.
We will make a folder call it ducks.
You can call it module but for the fun of it today it will be ducks in a file.
Let's first grab from the action type -- the constants.
Let's delete and pull it into our new file and we will add export const and we will have
to add action types here and add show.
Great.
Oh, hello.
OK.
And let the deleting begin.
We get to now delete the constants folder.
Fantastic.
Next up is actions.
We will grab from the actions and bring it into our new file.
Here we will add actions= this and don't forget you now get to delete the actions folder.
Bye, actions.
And last we get to merge in and handle the reducer.
We will grab all this code and bring it over to the ducks.
This part is the code that loads the initial state and that will stay as it is.
I think I want to change this reducer here because currently it does nothing, as I said,
other than returning the initial state and we can fix that actually, pretty easily, by
turning it into a switch statement which I have right here.
Last but not least, let us delete the reducer's folder.
OK.
Everyone, right here, in 21 lines of code officially contains all the functionality
we built up before.
It runs exactly the same way but it is modularized into a clean doc or ducks for optimized state
management.
Remember when we created all of those folders and all these files and it looked something
like this, messy, messy, messy.
They were fragmented and separated from one another.
Compare that mess that that was to this new file which contains all of the same capability
in a legible fashion and I will call that a win.
Hopefully, you do too!
In fact, such a little amount of code that it is able to fit into a single-slide right
here.
I have to say it was pretty easy and painless to create even in front of a nerve-wracking
environment.
That is the art of duck.
The art of ducking is structure an app to become more modular.
It is a sexy buzzword in this community but think about it.
It is great thing because it is obvious which piece of Redux is handling rich functionality
and you no longer have to scroll through masses of files before finding the one you are needing
to work on but this is only one way of structuring your Redux.
There are plenty of other options.
The simple fact I am encouraging you to explore the ways to creatively define what structures
means for you means the structure loving person I once was is leveling up to embrace ambiguity
and doing whatever is best for your code is the right way.
Ducks remove boilerplate and can remove a function's functionality to a reducer.
I have been on dev teams who adopted this structure and we were really happy with it.
Maybe you will be too.
In conclusion, I hope you are walking away from this talk feeling React and Redux is
not so confusing and maybe you were convinced by this duck's organization.
Thank you for listening.
My name is Lauren Lee and I would love to hear about your favorite organizational procedures
or chat further if you have quacky proposals.
Here is the original GitHub, arrest me to learn more, if you would like it.
Thank you.