Build Youtube in React 36: consecutive requests for related videos
1 Overview
Let’s continue where we left off and continue with creating consecutive requests.
2 Displaying related videos in Youtube
2.1. Consecutive network requests in Redux saga
Let’s create a new function inside src/store/sagas/watch.js
that will make the consecutive requests:
You probably noticed that we have some duplicate logic. We have parsing logic in our WATCH_DETAILS_SUCCESS
reducer
and in function
above.
In both places, we are filtering out the search response
and get the video ids
of the related videos. This is one of the huge drawbacks of consecutive requests. Sometimes they require parsing the response of the previous request. This is sort of an anti-pattern but cannot be avoided in this case unless we update our API
.
The Youtube API
does not support GraphQL
, so we I guess we have to live with these two duplicate lines of code. Now you might say, we might as well pass the parsed related video ids
down to our reducer. Like so we would not have duplicate logic. Well yes, but then we are parsing a network request response in one of our sagas
. This is not ideal either.
So you see, building Youtube
with the given REST API
has its caveats.
After we have the related video ids
, we create a new video detail request for each id
. Fortunately we can recycle our buildVideoDetailRequest
function
. We are using the all
effect which has a similar semantics like Promise.all
. Once we get the response, we dispatch the VIDEO_DETAILS_SUCCESS
action if we are successful. If we fail, we dispatch the VIDEO_DETAILS_FAILURE
action.
We fire theses requests, once we have the result from our fetchWatchDetails
requests back.
We do this after we dispatch the WACH_DETAILS_SUCCESS
action. Like so, we can chain the requests together.
2.2. Video details reducer
Just one more thing to do, we need to store the responses of our consecutive requests in our global state. Since we just fetch some videos, we put the results into our big videos dictionary, ie. in state.videos.byId
Let’s write our little reducer
function
first.
Put the following code in your src/store/reducers/videos.js
file.
Since we are using the all
effect, we get array of responses back. As a security precaution, we first filter the responses by its kind and extract only the responses of type youtube#videoListResponse.
This will be handy later on, believe me. After that, we build a small video “dictionary” where we associate each video with its video id
. We are using the Array.prototype.reduce
function
for this.
Once we are done, we merge our new videos into our videos.byId
object.
Now we only need to make use of it in our switch-case
statement.
We just need to add an additional case statement
.
2.3. Adding a related videos selector
Now that we have our videos in our state, we should provide a selector
to decouple our components from the global state.
As recommended by Dan Abramov, we colocate our selectors with our reducers.
We create a little helper function getRelatedVideoIds
which returns
the related video ids
for a specific video. We just a ternary expression
here to be safe in case we don’t have a related object
for a particular video id yet.
After that, we create our selector
where we iterate over the related
video ids
and pick the appropriate video from our state.video.byId
dictionary.
Theoretically there could be a case where we know the related video id
but where we haven’t loaded the video yet.
Therefore, we append a filter(video => video)
expression which kicks out all null
values in the array we return
.
2.4. Pulling related videos into WatchContent
Wow that was a lot of work. Let’s make use of it in our WatchContent
component and pull the related videos from our Redux
state in.
Let’s pass our new prop down to the RelatedVideos
component where the actual work is done.
2.5. Updating related videos component
Our RelatedVideos
component currently only contains hard-coded content. We need to change this.
First we check whether we the videos passed as props
and if the videos array
is also not empty.
The first element in the videos
array will be displayed in the NextUpVideo
component. All other videos (i.e. all videos starting from props.videos[1]
) are put into a horizontal VideoPreview
component.
We pass the VideoPreview
component a pathname and a search prop
so the user gets redirected when he/she clicks on it.
Let’s see how well we’re doing. Click on the image to see it in high resolution.

Well, that’s not too bad. But there are two things we still need to do.
First of all we need a little bit of space between videos in the right column. Second, we don’t show the next up video yet.
2.6. Updating the spacing between related videos
Let’s tackle the spacing problem first. Since the top element in our VideoPreview
component is now a react-router <Link>
element, our SCSS
don’t do anything any more.
Here’s what we have.
We’re only applying a margin-top
to the direct descendants. The react-router
<Link>
element gets converted into an anchor tag (<a>
tag) which is an inline
element and which ignores all top or bottom padding / margins
. We can do a quick fix by just targeting all direct descendant <div>s
that live inside the <a>
tag.
I’ve made the margin-top
value a little bit larger, so it looks better.

2.7. Displaying the next video
Our NextUpVideo
component isn’t showing anything because we haven’t made it dynamic yet. This is a matter of changing one line of code. We just need to pass on the video
prop to the VideoPreview
component that lives inside the NextUpVideo
component.
2.8. Fixing issues with react-router
Have you ever tried to clicked on one of the videos in the related videos section?
Well, the new video
is shown, but somehow the related videos section remains empty.
Why is that?
If you click on the video, the only thing that changes in the URL
is the search
parameter.
So suppose you navigate from
“http://localhost:3000/watch?v=J2X5mJ3HDYE
“
to
“https://www.youtube.com/watch?v=ab2y9VP1_6s
“
the only thing that changes is the search parameter v
. So React
will re render the components but no lifecycle hooks
like componentDidMount
or componentDidUpdate
is called.
But this is where we perform all our network requests.
To get around this issue, we need to force Watch
to unmount and remount again, our lifecycle methods reach out to the network.
To do so, all we have to do is to pass something to Watch
as props
that changes whenever the search params change.
Add the following code in your App.js
file.
The location.key
element changes every time the URL
is updated somehow (path changes, search parameter changes).
3 Wrap Up
Nice, that’s it.
We successfully display the related videos.
Click on the image to see it in high resolution.

However, we currently don’t provide detailed information on the video’s channel. We will do that in the next tutorial.
We also need to fetch the comments as well.
As always, thanks for reading.
You can find all the 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