Build Youtube in React 18: Adding Redux and redux-saga
1 Overview
1.1 Introducing state
Now that we have built out our Youtube MVP
components, we can think about how we get actual videos in there. Otherwise the app would be kinda boring.
Our app will make use of Youtube’s Data API v3, an API that allows us to fetch information on videos, categories, comments and so on.
Now let’s suppose we already fetched data (video categories, video view counts, …) from the Youtube endpoint. How do we store it?
We could store it in the component’s local state, but this will get quite messy because some of the data is shared by multiple components.
Therefore we will use Redux
, one of the most popular state management libraries out there.
1.2 Redux principles recap
If you worked with React
before, chances are you already know Redux
. I will not go into extreme detail how it works, I’ll just revisit some of the basic aspects. Otherwise, we need 100
tutorials to finish the app. If you are not familiar with Redux
, I highly recommend checking out the official documentation. It is very well written and quite straight forward. I also recommend checking out the Redux video course by Dan Abramov on egghead.io.
Redux
follows three principles
- The global state of the application is stored in an object tree in a single store
Redux
state is read only (unidirectional data flow). The state can only be changed by dispatching an action- The store is updated by pure functions (i.e. functions without side effects). Those functions are called reducers.
For brevity, we cannot go in depth on how Redux
works. Again, please check out the official docs.
2 Setting up a Redux store in React
2.1. Basic setup
We need two additional packages. The first one is Redux
itself and the second library is the binding between React
and Redux
. Redux
itself is standalone and is not directly tied to React
. That’s why we need a package to glue them together
First things first. let’s create a store
directory inside src
where we will put everything related to Redux
. We will need a directory where we put all of our actions and a directory where we put all of our reducers.
- Create a new directory called
store
insidesrc
- Inside store, create a
actions
andreducers
directory. - Add an empty
index.js
file both inside theactions
and thereducers
directory
Nice, we now have the basic structure for Redux
set up.
To create our store, we need a reducer. So head over to you index.js
file inside the reducers directory and put the following code there
This reducer does – you guessed it – nothing. But we can see how a reducer is supposed to look. The reducer is a pure function that takes in two parameters: the current state and the action that was dispatched. An action is just a Javascript
object that has a type
property. The type
property’s value should be a unique string
that describes what has happened in the application.
Depending on the action’s type, the reducer is supposed to calculate the next state based on the current state. Bear in mind that the Redux
state is immutable, so our reducer may not change the current state, but must always return a new object if something was changed.
Right now, our reducer does not do anything because we always return the state we currently have. The initial state is set to an empty object.
2.2. Configuring the redux store – adding middleware
To create a Redux
store, we need a reducer. We already have that. Nice.
The actions dispatched via Redux
are synchronous. However, in order to reach out to the network, we need the ability to dispatch actions asynchronously. One of the most popular ways that allows us to do that is redux-thunk. Redux-thunk is kinda nice, but will not be sufficient for our purposes.
Believe me, I know that because I first built this application using redux-thunk
and it got quite messy. To load the necessary information from Youtube's API
, we will be sending multiple requests out concurrently and sometimes even chain them. Now technically, we could do this with redux-thunk
, but believe me when I tell you it was a huge pain. I know that because I later refactored the entire logic to use a more powerful middleware called redux-saga.
Sometimes I hear people say, that redux-thunk
is fine for simple applications. Well this may be true, but is your application really going to be simple?
What happens if you want to add additional functionality? Then you must refactor it and you’ll have quite a miserable time. So why not build the application with additional complexity in mind and pick a powerful solution in the first place?
Sure, we might write a little bit more code and we must learn a few new concepts, but we will have a very easy time to to maintain and extend your application. Don’t be lazy and always think about extensibility and maintainability when you build something. You don’t wanna make your life miserable after all. But that’s just my two cents 😁.
Also bear in mind that there are other alternatives such as redux-observable. This would also have been fine as well. Just pick something that is not too limited. I kind of like redux-saga
because it is quite straight forward.
2.3 Installing redux-saga in create-react-app
Head over to your terminal and run
Now that we installed redux-saga
, we need a directory where to put our sages.
- Create a new directory inside
src/store
and name itsagas
- Create an
index.js
file inside of thesagas
directory you just created
Put the following code inside sagas/index.js
Our sagas will take care of all the asynchronous actions that are dispatched. To create a store, we bundle all our sagas into one single saga which we can call root saga. The concept is sort of comparable to combining reducers in Redux
.
The first time I saw this, I didn’t understand anything at all, but let’s just finish the setup and then later on we can talk about what redux-saga
really is and what function*
is.
2.4 Setting up Redux with redux-saga middleware
Almost done. Now, we have everything we need to create our store. We have a reducer (that currently doesn’t do anything) and we have a root saga that doesn’t do anything as well.
- Create a new file called
configureStore.js
insidesrc/store
This file will do the redux store
setup and plug in our redux-saga middleware
. We put this into a separate file because we want a clean separation of concerns.
Here’s how you create a Redux
store with the redux-saga
middleware.
We first call a function from redux-saga
createSagaMiddleware
. After that we plug our middleware into our store by passing it as an argument to applyMiddleware(sagaMiddleware)
.
Now, we only need our saga middleware to run our root saga (line 13) which is supposed to bundle all of the small sagas we will create in the future.
So far so good.
2.5 Plugging Redux DevTools into our Redux store
There’s still one thing we should do. While in development mode, we would like to see what actions are dispatched to our store and be able to go back in time. And no, you don’t need a the crazy professor from Back to the Future for this, you only need Redux DevTools extension.
Redux-DevTools is an extension you can install into you browser that shows you what kind of actions were dispatched.
Now to make this work, we need a little bit of config.
Change your redux store
setup like so:
What we do is, we get an object called __REDUX_DEVTOOLS_EXTENSION_COMPOSE__
from our window object. This is something that the Redux DevTools
extension adds to your DOM
if you install the browser extension. If it’s not there, i.e. the extension is not installed, we just take the compose
function provided by redux
. By wrapping our applyMiddleware
in a composeEnhancers
call, we can travel back in time and replay actions that have happened using the browser extension.
Now I won’t go into detail what an enhancer is how that works because that’s kind of out of the scope of this tutorial. You’ll probably never write an enhancer
yourself, you just have to know how to plug it in.
Don’t forget to install the browser extension as described in the official documentation. Otherwise this setup is of no use.
2.6 Adding configured Redux store to our React application
The only thing that is left to do, is to plug our store into our application. Head over to your index.js
file in the root directory of your project.
Replace the contents of it with this.
Note that we wrap our application into a Provider
component from redux
. We need the Provider
component so that we can access our store in our components. The Provider
component basically allows Redux
to apply its magic, similar to how BrowserRouter
allows our application to use react-router
.
Please run your application to make sure that everything is working
Everything should look like before, but under the hood a lot has changed.
3 Wrap Up
Wow, that was a lot of work. This is probably one of the hardest tutorials in this series. It’s going to get easier, that I can tell you.
Doing all this config stuff can be pretty daunting. Please bear in mind that you don’t need to have an in-depth understanding on how all the tools introduced here work.
You just need to know how to set them up. In fact, a lot of people (including myself), don’t understand the entire magic that’s going on under the hood.
I always have to look it up when I do it.
So the next time, you set up Redux
in React
, you might come back to this tutorial because our setup is not specific to the Youtube
app we’re currently building.
As always you can find this project’s code on Github.
Follow @productioncoderPlease follow me on Twitter @productioncoder to stay up-to-date. I’m happy to receive feedback of any kind.
If you are interested in more high-quality production-ready content, please enter your email below.
Recent Comments