Server Rendering with React and React Router v4

By | August 26, 2019


In this video, what we are going to do is
we are going to learn about server-side rendering, server-side rendering with react. And then we will tie in React Router. So we will get a server rendered app with
react and React Router. So first, let’s go ahead and define what server-side
rendering or isomorphic JavaScript or universal JavaScript actually is. Here’s the basic idea. When your user types in the URL into their
web browser and hit Enter, your server is going to see that it just got a GET request. In this case, we’ll say it’s to the index
page. What your server is then going to do is instead
of spitting back like JSON or something, it’s actually going to render your app’s main component
and wrap it inside of a standard HTML document, you know, like doctype HTML head body, and
it’s going to send that whole thing back as a response. The browser is then going to get that HTML
document, it’s rendering engine is gonna go to work, and then the page is going to finish
rendering. At this point, the page is viewable and the
browser is going to start downloading any scripts. Once it starts downloading scripts, it’ll
see that it needs to download react. And then at that point, react is going to
take over, and then your page will be interactive. So let’s actually see that process in action. Here is the final app that we’re gonna be
building. So you’ll notice here if we go to the Network
tab, once I hit Refresh, we’re going to get a few different things. You’ll notice the very first one is just an
HTML doc and the second one is our bundle.js file. So if we click on this one, you’ll notice
that the response that we got was just this HTML document. So that’s the first step. The server saw, “Hey, it looks like the user
is wanting to get the index page. Let’s go ahead and spit back to them an HTML
document.” And then eventually, what happened is our
bundle.js file loaded, which then went, fetched react, fetched all of our components, and
then made the page interactive. So, again, notice that with server rendering,
the response the browser gets from the server is the HTML of your page that is ready to
be rendered. This is really different from client-side
rendering, which just spits back at blank HTML document with a huge JavaScript bundle. By sending back a finished HTML document,
the browser was able to show the user some UI without having to wait for the JavaScript
to finish downloading. So why is this beneficial? Well, as we just talked about, the perceived
performance might be a little bit better because instead of having to wait for that JavaScript
to finish downloading, the user kind of just gets the whole app or the whole UI upfront. It’s nice for code reuse. You can have your server code reusing the
same code as your client code. And then the biggest benefit is probably around
SEO, which you may or may not care about. When building a server-rendered app, though,
there are more circumstances that you have to consider. Things like which code will be shared? Is there initial state that needs to be shared? How do you handle routing on both the server
and the client? When you’re building for just the client,
that can kind of be your entire paradigm. But when you’re building a universal app,
you have to keep in mind that sometimes the app is going to be rendered on the server
and spit back, and then sometimes, once the user actually has the app downloaded, the
client-side router is going to take over. So with that in mind, let’s go ahead and start
breaking down the things that we’ll need. And then we will slowly start building an
app that again looks like this where we load the app from the server and then react and
React Router takeover. And then what we can do, this is all just
client-side routing stuff that you’re used to. But if we start out on this page, what’s going
to happen is before the server sends us back a response, it’s going to go ahead and fetch
all of these popular repos from the GitHub API. So even though this is a pretty basic example,
it really does everything that a server-rendered app with React Router would wanna do. So I debated if I wanted to build this example
completely from scratch including like the package.json and the web pack files. The answer I came away with was kind of, so
here’s the app we’re going to be starting with. Notice it’s just going to have a package.json. That’s basically it. So the packages that we’re eventually going
to be using are these right here. We have a bunch of babel/babel react stuff. Nodemon is going to be for our server updating. Webpack is our bundler. And then we are going to use all of these
as our normal dependencies cors, which we’ll use on the server. We’ll use Express to handle the requests to
the server, fetch so we can fetch on both the browser as well as on the server and then
a bunch of react stuff and then we will just serialize any responses that go from the server
to the client. So if this was kind of over your head, don’t
worry about it too much. I’ll talk about it when we actually use it. But if you are following along, make sure
you download all of these devDependencies and then all of these dependencies. Also notice these babel settings right here. So we’re going to use the “env” and “react”
presets and then the…really the only kind of transform we’re going to need that’s not
react is going to be just our object-rests-spread plugin. The only other thing to note about our package.json
file is our start script. When we run npm run start or yarn start, that’s
going to go ahead and watch or run webpack -w, which is going to watch all of our code. And when it changes, it’ll run it through
our transforms. And then nodemon server is just going to keep
our server running and updating it once we actually start it or change it. So because I mentioned we’re using webpack,
where was that at? Right here. We actually need to go and set up our webpack.config.js
file. So let’s go ahead and make that. So create a new file called webpack.config.js,
and because, again, this isn’t a webpack tutorial, I’m just going to paste this in and then we’ll
talk about it. So you’ll notice that we have two configurations
here, the browser configuration and the server configuration. And then we are just returning an array of
both of them down here. What the browser configuration is going to
do is this is going to be our entry point, kind of like the main entry into our app. What Babel is going to do is it’s going to
run it through a babel loader, which as we just barely saw is going to make these transforms
on it right here. That’s then going to be spit out into a public/bundle.js
file. And then the only other thing to mention with
this is this plugin right here. This is basically going to give us a property
on the global namespace, which just says is browser set to true so that way we know eventually
if we are on the browser or on the server when we are dealing with a specific file or
a specific component. So the server code is going to be really similar. Our entry is going to be source/server/index.js. Notice we are just targeting node. What this line does is it basically says,
“Hey, don’t go ahead and bundle all of the node modules that these server needs because
that’s unnecessary.” The output is going to just a server.js file
on or in our root folder. It’s also going to be run through the same
babel loader and then we will have isBrowser set to false. If you don’t want to copy all this by hand,
what you can do is head over to github.com/tylermcginnis/rrssr. And you can download all of the code for this
whole video right there. All right, so let’s keep going. So what we know about our app from this webpack.config
file is we’re going to have a source folder with all of our code in it. We’re going to have a browser folder, which
is going to have an index.js file. And we’re also going to have a server folder,
which will have another index.js file, which is kind of the entry point to the server and
also the browser. Another folder that we’ll have in there is
instead of being on the browser or the server, let’s go ahead and have a shared folder, which
is going to be all the code that’s going to be shared between the two. So what we will do is let’s go ahead and create
our source folder. And then under here, as I just mentioned,
we’re going to have a browser folder. We’re also going to have a server folder. And then lastly, we are going to have a shared
folder. And according to our webpack config file browser,
we’ll have an index.js file. And then our server will also have an index.js
file. So we will create both of those right now. And then now, let’s actually start doing something
that’s not just configuration stuff. So, if you remember, when we broke down the
initial server-side rendering process, there were really three items we were going to need
first. One, a react component, even just a basic
one that renders “hello world” for now. The second was a server which is going to
spit back our basic react component after it’s wrapped it in some sort of HTML structure. And then the third one is a react app, which
is going to pick up from where the server-rendered HTML left off and add in any event listeners
to the existing markup where needed. So what we can do is let’s go ahead and inside
of our shared folder, let’s create a new file called App.js. This file is going to act as our main component
throughout our whole application. So eventually, we’ll have some routes in here. But for now, let’s just go ahead and just
render “Hello world.” So what we’ll do is let’s have an app, which
is going to extend component. As always, it will have a render method. And before we forget, we want to go ahead
and export default app. So all this is going to do for now is the
UI will just say, “Hello world.” So very basic for now. Now, we have our basic component set up. Let’s go ahead and start creating our server,
which is going to spit back this basic component, our app component after it’s wrapped it in
some HTML structure. So what we’ll do is inside of our index.js
file, let’s go ahead and import a few things that we’ll need. First, let’s grab an import express as well
as cors. And then what we’re going to do is let’s actually
come in here and invoke Express creating ourselves an app. And then what we want to do is we want to
use cors. If you’re not familiar with Node or any of
this stuff going on, kind of just stick with me. This will make sense here in a second. So what we want to do is we want to serve
up anything that’s inside of a public folder, because eventually what’s going to happen
is if you remember back to our webpack.config file, what we’re doing is we are putting all
of our browser configuration code, basically, all of our app that gets ran through the react
transform and our es6 transform and whatnot, all of that code is going to be put inside
of a bundle.js file, which is inside of a public folder. And so when we render our HTML, we’re going
to go ahead and tell it to download the script located at bundle.js. And the reason we can do that is because we’re
going to go ahead as we just barely did on the server and say, “Hey, go ahead and make
available anything inside of the public folder.” So you’ll see how all that ties together in
a sec. And then we actually want to start our server
and say, “Go ahead and listen on…” We’ll say port 3000 for now. And then all we need to do here is we will
say, “Server is listening on port…” we’ll just hard code in 3000. All right. So we have our server and we have our component
that we’re going to render. Now what we wanna do is we wanna make it that
anytime our server receives a GET request, we send back the HTML skeleton along with
the markup from our app component inside of it. To do this, what we’ll do is react has a renderToString
property on it. And what it does is it takes in a react element
and it returns an HTML string. So what that looks like is back inside of
our server, what we can do is let’s import a few more things. We want to import renderToString from react-dom/server. And then let’s go ahead and grab our app component,
which is located inside of shared/App. So now, as I just mentioned, whenever we get
a GET request, what we want to do, so we’ll say App.get* for whenever we get anything. This function is going to be invoked. We’ll get a request object, a response object
and then next. And then what we will say is we wanna go ahead
and create some markup. What that markup is going to be is our app
after we’ve rendered it to a string. And then now that we have the markup, we want
to send that back inside of an HTML body. So what we’ll do is we’ll come in here and
say res.send. And then you can act like this is eventually
going to be the HTML that the browser is going to get. So I’m just going to paste the main body in
here. So you notice what we have is a doctype HTML
head kind of like we’re normally used to. And then what we want to do is let’s go ahead
and render a div here. We’ll give it an ID of app, which we’ll go
ahead and use later so we can mount our client app to it. And then inside of here because we were in
a template string, let’s just go ahead and throw in all of the markup. And now, we have our HTML that we’re spitting
back including our react markup. So we can actually go ahead and test this. One thing I forgot to do up here is because
we are using JSX there on line 15, we need to go ahead and import react. And then now, if you haven’t already, go ahead
and start your server. And when you go to the browser, you’ll notice
that we’re getting “Hello world.” And if we look at the network tab, what’s
going on is we are just getting that from our server. So now what we wanna do is we wanna make it
so when we get a response back from the server and we show this HTML, then what we wanna
do is we actually wanna go ahead and download our bundle.js file so that react can then
take over and add any interactivity to our app that it needs to. So what we’ll do is, if you remember, where
our final browser code is going to end up is it’s going to end up in public/bundle.js. And all of that code is going to contain react-react
router and really all of our components that we want to render. So what we’ll do is inside of our server,
inside of the head, we want to tell the browser where it can download that code. So we will say script source is going to just
be /bundle.js. We’re going to add a defer prop to it so that
we don’t block any rendering. And then now, because of this line right here
as well, bundle.js is just going to be the file that webpack is going to give us after
it bundles all of our code together and transforms it. So now, we know that the server is going to
be aware of our client-side code. But now what we want to do is we want to make
it so that when react downloads, it will pick up from where the server-rendered HTML left
off and as I mentioned a few times, add in an event listeners to the existing markup
where it’s needed. So this one sounds more difficult than it
actually is. Typically, what happens when you want to tell
the browser about your react app is you call react on that render passing at the element
and the download you want to mount to. So what that looks like is let’s head over
to our index.js file inside of the browser folder. Typically, we always grab a react because
we’re going to be using JSX and then you grab render, which is going to come from react-dom
and then you grab the element you want to create, which we will grab app. And then what you do is you call render, first
creating the element and then second, telling it where you want it to mount to. And we will say GetElementById (app) because
if you remember in our server, that’s the ID that we’re spitting back. There is one change we need to make here. Because we’re using server rendering, instead
of using render, what we actually want to use is hydrate. And the reason for this is what hydrate is
going to do is it’s going to tell react that you’ve already created the markup on the server
and instead of recreating it on the client, you should just preserve it and just attach
any needed event handlers to the existing server-render markup. So it’s basically saying, “Hey, react, don’t
recreate this template. Don’t recreate this markup. We’ve already done that. Just do anything that you need to do without
actually reproducing all of the work the server has already done.” So now what should happen is when we get a
GET request to our server, we’re creating some markup, we’re spitting it back down,
then this line is going to run. The bundle.js is going to run. React is going to download. It’s going to say, “Hey, I need to go ahead
and hydrate the client.” At this point, it’s really not going to do
anything because we’re just saying hello world. But eventually, this will do a lot more like
adding event listeners, adding interactivity, and then we’ll be good to go. So let’s see this in action. So, head over to your app. You’ll hit refresh. You’ll notice we got a response here of HTML. And then what happened is bundle.js went and
downloaded, which was all of our react code. All right. So we’re like 20 minutes in and we have hello
world. I don’t know if that’s a great thing or a
terrible thing, but that’s where we’re at. We’re gonna keep going. So one gotcha or one thing you need to keep
in mind with server-side rendering with react is that the markup you create on the server
needs to be the exact same on the client or else there’s going to be some issues. So what if inside of our app.js file, instead
of saying hello world, let’s just say hello whatever this.props.data is. So now whenever we create our app element,
we need to pass it data, obviously. So, where we do that is inside of browser/index.js. So let’s go ahead in here and pass in data
as just a string. This is Tyler. And then on the server, what we can do is
when we call renderToString, we can do the exact same thing in here as well and pass
in Tyler. So now when our app reloads, instead of seeing
hello world, we see hello Tyler, cool. But what if the server we pass down Tyler,
but on the client, we pass down Mikenzi. You’ll notice something interesting here. When I hit refresh, notice, we initially see
“Hello Tyler” and then it changes to Mikenzi. What’s happening here is the server is spitting
back code that looks like this with hello Tyler. But then once React takes over, once the bundle
downloads and React takes over, it changes that to Mikenzi. And you’ll also notice we get a nice little
error here saying “text content did not match. Server was Tyler. Client was Mikenzi.” In this case, it worked out because the app
was so simple. But in more complicated scenarios, it’s not
gonna work out as nicely. In fact, here’s what React has to say about
this. “React expects that the rendered content is
identical between the server and the client. You can patch up differences in text content
but you should treat mismatches as bugs and fix them. In development mode, React warns about mismatches,”
as we’re seeing here. “There are no guarantees that attribute differences
will be patched up in case of mismatches. This is important for performance reasons
because in most apps, mismatches are rare and so validating our markup would be prohibitively
expensive.” When you’re just rendering a component with
no data, it’s not really difficult to have the server rendered and the client rendered
markup be the same as we saw when we were just rendering our app component. But when you add in data, it can get a little
bit more complex. You need to make sure that the components
are rendered with the exact same props on both the client and the server. So let’s take a look at how we would do that
without actually hard coding in our data prop. One thing we do know is that since the app
is going to be server-rendered first, any initial data or app needs is going to have
to originate on the server. Now, with that in mind, in order to make sure
the server and the client are the same, we need to figure out a way to get the same data
that originated on the server down to the client. Well, there’s a pretty old-school solution
for this, but it works out pretty well. What if we came in here inside of our server
and, again, we don’t want to hard-code Tyler in here. So what we’ll do is let’s just create a variable. We’ll give it a name of Tyler for now because,
again, the server is going to be rendered first. So this is where the data is going to originate
or at least the initial data. And then we’re going to pass down name as
the data prop. And now the question is how do we also get
this exact same name to the client? Well, what if we came in here and said script._INITIAL_DATA
it’s going to equal. And then let’s go ahead and serialize this,
which we already downloaded. If you haven’t, you can go ahead and download
this right now. Serialize.JavaScript. And then now what we want to do is let’s say
serialize. We’ll pass a name and then we will end the
script. So what we’re doing is the way we’re getting
this data down to the client from the server is just by adding it to the global object. Now inside of the browser, what we can do
is instead of hard-coding Mikenzi in there, what we want to do instead is the data is
going to be whatever window.__initial_data. So let’s see if this works. Now, when our server rendered app renders,
we get Tyler and window.__initial data__ is Tyler as well. So we can pick that up on the client. All right. So now let’s actually start building something
of substance. Odds are you’re never going to have just static
initial data like we have now. Your data will most likely be coming from
an API somewhere. So let’s modify our server so that it fetches
some data before it returns the HTML. Remember, the end goal here is to return some
HTML that has data from the GitHub API inside it, so specifically, the most popular repositories
for a specific language. So what we’ll do is inside of our shared folder,
let me close all of these, let’s go ahead and make a brand new file called api.js. And I’m actually just going to paste this
in here because it isn’t super relevant. All we have now is a fetch popular repost
function. It’s going to take in a language which defaults
to all if none is supplied. We then have an encoded URI, which is just
this, the result of calling encodeURI passing in this long string. Notice we pass in the language. We call fetch. We then go ahead, change that to .json. Grab the items, property, and that’s going
to be what returns. And then we just console.log or console.warn
the error if there are any. So this is just a basic function using fetch. If you’re familiar with Ajax at all, this
is going to be super familiar to you. Now what we wanna do, as I just mentioned,
is before we actually return the response from the server, we wanna go ahead and get
some of the information from the GitHub API and then pass that down as the data rather
than just the name. So what we’ll do is let’s import the function
we just barely created, say fetch popular repos is coming from shared/API. And then now, instead of just straight away
returning all that, let’s go ahead and in here, invoke fetch popular repos. That’s going to return us back some data. And then now what we can do is let’s go ahead
and just paste all of this inside of this callback. Name is no longer going to be needed. So we can delete that. Data we already have it’s coming in as data. So we can go ahead and pass that to our app
component as well. And then now, instead of passing a name to
the window object, let’s go ahead and give it the data that we’re getting from the GitHub
API. And so now before we take a look at this,
let’s actually go ahead to the browser real quick. And eventually what’s going to happen is we’re
going to get that data and we’re going to spit it into a grid. So what we’ll do is let’s create a brand new
file. We’ll call it grid.js. I’m also going to paste the code in here so
we can see it. Notice we’re grabbing React. And then all we’re doing is we’re getting
this.props.data, which is the repos. We’re mapping over those and then just creating
some UI. So we have the name, the GitHub user’s username,
how many stars the specific repo has, things like that, so pretty basic. And now inside of our app.js file, instead
of just rendering hello, this.props.data, let’s go ahead and grab grid, which we just
barely made. We will render grid and then data is going
to be coming from this.props.data. All right, so let’s see this in action. So what we should expect to see, so we hit
Refresh, our server response is going to take a little bit longer because it’s going and
making that request. Then what we get back is this stuff right
here. Notice that the UI rendering is a little bit
more complex. We’re still sticking all of that data on initial
data. And now, we get a UI that looks like this. We’re slowly building up to what we actually
need. And the next step really is just adding routing. So if you’re familiar with React Router, you
know that it kind of has a dynamic routing paradigm to it, meaning you render more routes
as your app renders. The problem with that is we need both our
client and our server to be aware of our routes. So we can’t really use dynamic routing or
else the server will just be oblivious to our routes. So what we’ll do is we will move back to the
central route config paradigm that reactor router used to have. The client will use their routes because it
obviously needs to know which components to render as the users navigates around our app
and the server because it needs to know which data to fetch when the user requests a specific
route or a specific path. So let’s go ahead and create a central route
config inside of shared. Let’s create a brand new file. We’ll call it routes.js. And the first thing we’ll do is grab the components
we’ll need. And then we want to represent our routes as
an array. So what we’ll do is we will have an array. Each item in this array is going to represent
a route. And all of its information, all these properties
are going to be things that we will pass to a route component, a React Router. So we will have a home component, which we
will create here in one second. And then the second route is going to be /popular. And then we will use a URL parameter of ID
and the component that’s going to be rendered when that path matches is the grid component. So before we get any further, let’s just go
ahead and create our home.js file real quick. And all we will do is we will return a functional
component that will have a div and inside of it, let’s just say select a language. So if you remember, I mentioned earlier that
the reason the server needs to have access to a central route config is because it needs
to know which data to fetch when the user requests a specific path. What that means is that we’re going to put
any data requests that a specific route needs in the route object itself. So basically what’s gonna happen is the server’s
gonna say, “Hey, it looks like the user is requesting the /popular/JavaScript route. Is there any data that needs to be fetched
before we send back the response?” “There is.” “Okay, go ahead and fetch it first and then
send back the response.” So inside of our routes, what we’ll do is
let’s add a FetchInitialData method to our route. And then what we will do is we will invoke
this fetch popular repos method we created earlier, which is coming from our API file. And when we invoke FetchInitialData, what
we’ll do is we will pass at the current path. And what that will allow us to do is it will
allow us to pass the specific language to fetch popular repos by doing, let’s just say,
path.split and then we’ll pop off that last one. All right. So now what we’ve done, and let’s not forget
to export our routes here, is our routes, so array contains everything we need to know
about the specific route including if that route needs any data when it renders. So let’s head back to our server and actually
make this change, so inside of server/index.js. The first thing we need to do is figure out
which route, if any, matches the current requested URL to the server. For example, if the user requests the index
page just slash, we wanna find the route which matches slash inside of our res array. Luckily, for us, React Router exports a match
path function that it uses internally to match the locations to routes. So we can do that basically to recreate that
same functionality. So what we will do is let’s go ahead and import
match path and that’s going to be coming from react-router-dom. Let’s import our routes which are going to
be coming from shared/routes. And then now when we get a request, the very
first thing we want to do is find out which route inside of our routes array matches the
requests that we received. So what we will do is let’s come in here and
say active route is going to be whatever routes.find returns us. So we wanna find a specific route in our routes
array. And so what we’ll do is we’ll pass find a
function. We’ll get the specific route. And then we will call matchPath, passing in
the current requested URL as well as the route. And if this doesn’t give us anything, then
we will just make our route an empty object. So now at this point, we have the activeRoute,
which is either going to be this object or this object. And now what we wanna do is if this specific
route needs any data, we wanna go ahead and fetch the FetchInitialData method so that
we can go and grab that data. So what we’ll do is instead of just calling
fetch popular repos, we don’t need to do that anymore because we will create a promise. And then we’ll say if the active route has
a FetchInitialData method on it, then promise is going to be activeRoute.fetchInitialData
passing it. Remember the path and if not, then we will
just use promise.resolve so that we can then come in here and say promise.thendata. All right. So again, what’s happening here is let’s move
all of this stuff down first. There we go. So when we now have a GET request to our server,
we find whatever route matched the request that we got. We then see if there’s any data that route
needs. If there is, we fetch it. If there’s not, we just to promise.resolve. And then instead of hard-coding in here, the
fetch popular repos method, we may not want to fetch the popular repos. So we check first. And if we do, we fetch them. If we don’t, we don’t, and then everything
else remains the exact same. One thing we can do is we’ll come down here
and we’ll say, “If there’s an error, just move on to the next request.” All right. We’re getting close. Let’s go ahead and try this out. Now, so inside of our browser, let’s go to
/popular/JavaScript. Now you’ll notice all of the most popular
JavaScript repos are being fetched. And if we go to, let’s say, /ruby, then what
we should see is here we go, all of the popular Ruby repos. So now that we’re fetching the correct data
on our server based on the route the user requested, let’s go ahead and add in some
client-side or routing as well. So as always, we need to wrap our main app
component inside of React Router’s browser router. So we can do that inside of browser/index.js. We will import browser router, which is going
to be coming from react-router-dom. And then now just wrap our app inside of that
and there we go. So now because we’ve given control of the
client over to React Router, we also need to do the exact same thing on the server so
that they match, but because we’re on the server, it doesn’t make sense to render a
component called browser router. That’s obviously for the browser. So instead what we’ll do is we will use React
Router’s static router component. So inside of here, let’s import static router
from react-router-dom and we can just come in here and put a comma. And then inside of our renderToString, let’s
do static router, passing it the data inside of it. So the reason it’s called static router is
because the location is really never going to be changing on the server. Now static router is going to take in two
required props. Excuse me. The first one is going to be location and
the second one is going to be context. Location is the current location being requested
by the user. So we can go ahead and just use req.url here. And then context needs to be an object that
can contain any information about the render. We’ll use a blank object for now, but we will
eventually come back to that. All right. So we have a server-side router and then we
also have a client-side router. Now let’s actually go ahead and render some
client-sided routes. So inside of app.js, what we need to do first
is let’s go ahead and import routes from our routes file. And then we also want to import the route
component from react-router-dom. And because we no longer need grid, we can
get rid of this and let’s get rid of this as well because we no longer are going to
do this since we’re going to be rendering some routes. So what we’ll do is let’s call a routes.map. So we’re going to map over all of our routes. Each route will contain or can possibly contain
a path, an exact prop, a component which we will rename as a capital C and then just the
rest of this stuff. This rest is going to contain the FetchInitialData
method. And then what we want to return is obviously
a route. And this route is going to have some properties
to it. We’ll just go ahead and pass all these down. It’ll have a key because we’re using map,
which we can use a path for. Path is going to be path. Exact is going to be exact. And then what we want to do is we wanna make
sure that we pass these specific components being rendered, our FetchInitialData method
if it needs it. So that way we also inside of…or on the
client, we also pass this information to the component so we can also fetch the data if
it need it even on the client. So what we’ll do, because we need to pass
a prop to a component, we will use the render method. So this way we can go ahead and create the
element ourselves. So we will pass it all of the props, which
we’re going to be passed. These are going to be the routing props and
then we will pass it the rest of this stuff that is on each individual route item. So before we move on, let’s go ahead and add
a Navbar and a catch-all route to our app. This video is already way too long to walk
through both of these. So what we’ll do is let’s just create a navbar.js
file And you’ll notice all we’re doing here is we have some languages and then each contain
a name and a param. We are then looping over those languages creating
a NavLink for each of those. If it’s active, we make it bold. We go to /popular/whatever the param is and
then we pass in a name. And then as I just mentioned, we want to have
a 404 route. So if none of our other routes match, we hit
this 404 route. This means we need a brand new file. We’ll call it nomatch.js. All it’s going to render is a 404. And then inside of our app.js file, what we
need is a switch component to wrap all of our routes. And then at the very bottom here, if none
of these routes match, let’s go ahead and render a route, which will render our nomatch,
which we still need to import. And then we’re just going to spread all of
the router props onto it. So we’re going to grab nomatch from the nomatch
file we just barely created. And then now everything is looking pretty
good. Let’s make sure that this is still working. So if we go here, what we should get is select
a language. We never actually rendered our nav component
that we created. So let’s go ahead and import that. We have a Navbar coming from Navbar. And then inside of here, let’s make sure we
render our Navbar now. All right. So we get our Navbar. We get our home component. Here’s where we run into an issue. If we click on one of these, you’ll notice
that we get this error right here, “Cannot read map of undefined.” So here’s what’s happening. Before, what we were doing is we were passing
data as a prop to app then we pass that down to our grid component. But because we aren’t rendering our grid component,
we’re not actually creating that element anymore inside of app, since we’re rendering our routes,
instead, that data isn’t making its way to grid and therefore, props.data inside of grid
right here is undefined. So when it tries to map over those on the
server, it’s just throwing an error. So there are a few different ways to fix this. What we could do is we could pass the data
to the component when we render it inside of the render method. So instead of our app, what we could do right
here is we could figure out a way to pass it the data. The problem with doing that is we’re going
to be passing data to every single component even the ones that don’t need it. So instead what we’ll use is let’s go back
to that context prop that was used on the static router component. This is where context comes into play. Anything that we stick on this object that
we passed the context will be able to access later on in any component as props.static
context. So instead of passing data to app, let’s use
this context instead. So what we’ll do is let’s come in here and
we will say context is going to be data. We’ll wrap it in an object. And then we will pass context to context and
we can go ahead and get rid of this line right here because we’re no longer going to pass
data as a prop to app since we’ll be getting it on the server at least on the context. So now inside of our grid component, instead
of coming from this.props.data, as I mentioned, it’s going to be coming from this.props.staticContext.data. So let’s see if this works now. So if we try to reload this, notice that now
we are getting all the languages. So our app is at an interesting point right
now. This route appears to be working fine, but
you’ll notice, if you open up the console, we get an error. The reason for that is because the server-side
rendering of things is fine, but when a React goes to pick it up on the client, it’s running
into some issues. The reason for this is because just as we
did before on the server, we were passing a data prop to our app component on the client
as well. So inside of our code, you’ll notice on the
client, we are also passing down a data prop to app, which obviously isn’t working anymore
because app is no longer receiving this data prop. So what we wanna do is we want to somehow
figure out a way to get this data down to our grid component without using this data
prop. Well, it’s a property on the global object. So what we can do is just inside of grid,
we can come in here and grab it. But now the question is how do we grab it
because we’re already doing this.props.staticContext, but on the on the browser, static context
isn’t actually going to be a thing because that’s just a server thing because we’re only
using that when we are on the server. So what we can do, and this is where this
plugin is going to come into play, if you’ll remember, because of this line right here,
we have an isBrowser property we can use and we can check, which is on the global object
to tell us if we’re on the browser or if we’re on the server. So inside of grid.js, let’s go ahead and make
a constructor, lost some states. Let’s create a repos variable. And then if, let’s say, __isBrowser, then
what we want to do is repos is going to be coming from window.initialdata. And then let’s go ahead and just delete that
off of the window. And if we’re not on the browser, meaning we’re
rendering on the server, then we want to do this line right here. Repos is going to be coming from props.staticContext.data. And now let’s go ahead and add some state
to our component, say repos and then now, let’s go ahead and grab the repos down here,
which are going to come from this.state. So again, what’s going on here is if we’re
on the browser, repos is going to be on the window object. If we’re not on the browser, then repos is
going to be coming from props.staticContext.data. All right. This looks good. I kind of lied to you earlier. Sorry about that. Inside of here, the reason we were getting
the specific error we were getting was because we had a comma right there and not right there. But trust that, even with fixing this, if
we wouldn’t have separated the server rendering from the client rendering inside of grid,
we also would have gotten an error. So let’s go ahead and check now where our
app is out. It looks like everything is working well. So we hit Refresh, we get our server-rendered
page. And if we look at the client, everything is
good because React picked up where server rendering left off, which was adding in client-side
routing. All right. We’re almost done, I promise. So at this point, our server is all finished. It’s properly getting the requested path,
fetching any data for that path, and then sending back a nice server rendered response. It’s the client that is going to have some
issues if we poke around. So let’s go ahead and load up our homepage,
localhost: 3000. So everything seems to be working fine right
here. But now, let’s go ahead and try to click on
this JavaScript link. Notice that we didn’t really get anything. And on the client, we have a few different
errors, specifically cannot read property map of undefined. Remember, when we’re dealing with both the
server-side and the client-side rendering, server-side rendering is only at the initial
page’s load. After that, React Router is going to take
over. So what’s happening is when we first request
the app, everything is fine because the server is fine. Then when React Router is taking over and
we try to go to /popular/JavaScript, the app breaks because we don’t have the correct data. The good news is, to solve this error, we
can just kind of do as we’ve always done and fetch the data and ComponentDidAmount if we
don’t already get it from the server. So inside of our grid component, let’s make
a new method here. We will call it fetchRepos. What fetchRepos is going to do is it’s going
to take in a language and then it’s going to call this.props.fetchInitialData passing
in that language. That’s going to return us a promise with the
repos. And then we want to update the UI. So we will call this.setState. Repos is now going to be the repos we got. So now what we want to do is when the component
mounts, if we don’t already have this.state.repos. So if it is null, which it will be when we
didn’t get any repos from the window object and we’re not rendering on the server, so
basically whenever React Router takes over and we switch her out, then what we want to
do…let’s go ahead and call this.fetchRepos, passing it the URL parameter, which will be
this.props.match.params.id. And then now we can’t forget. Let’s go ahead and bind fetchRepos up here
inside of the constructor. So this looks pretty good. What I wanna also do real quick is just add
a loading prop here. So we’ll say if repos is a thing, then the
loading is going to be false. And if it’s not then loading will be true. And then now once we actually fetch the repos,
then we will go ahead and flip loading back to false and not true. Ad then up here what we can do is whenever
we fetch these, loading should become true. All right. So now inside of our render method, let’s
grab loading off the states. And we will just come up here and say if loading
is true, then return loading. All right. So this looks good. Now what’s happening is when our component
mounts, if we don’t already have the repos and again which we won’t, if React Router
took us to our route, there was no server-side rendering. So we were never invoking our promise up here
or FetchInitialData, which is coming from the route. So instead, we’ll have to do that on the client
inside of the ComponentDidMount. We’ll get the information set state which
will cause a re-render and then we’ll get all the repos just like we would have done
or we would have gotten if we were rendered on the server. So let’s go ahead and check to see if this
is working. So we request our main page from the server. We get it. Everything looks good. We click on JavaScript. It’s loading and then we get those. Everything is working perfectly. Just one small thing that’s still broken. Notice if we click on Ruby, nothing’s happening
because when we click on a new route, our component isn’t remounting. It’s just changing the props of the URL parameter. So the very last thing, I promise, is inside
of here. What we want to do is we want to say when
the component will receive props. That’s going to give us the next props. What we’ll do is let’s grab match as well
as fetchInitialData from the current prop and then we will say if the nextProps.match.params.id
so the URL parameter does not equal the current URL parameter. Then what we want to do is we will call fetchRepos
again, passing it the next URL parameter. All right. So this looks good. Let’s go ahead and try this. We just passed an hour long at least pre-edit
and I’m feeling great. All right, so we get our UI here just to refresh
just in case we click on all, loading, and then we get all of the popular JavaScript
repos we can get. Click on Python and look at that, everything
is working great. And I really hope we have no errors. We don’t. Perfect. So let me walk through this one last time. When a user makes a request to our server,
we figure out what the active route was. And once we find that route, then what we
do is we figure out if that route needs any data. If it does, we fetch it. If it doesn’t, then we don’t do anything. Once we get the data or once the promise resolves,
we go ahead and renderToString a static router. We pass it the current location so that the
router will know which path the user is requesting and then we pass it as context any data that
we have. Then what we do is we make it so when the
app downloads on the browser on their client, it’s going to request bundle. What that’s going to do is that will eventually
get React, React Router and all of our client-side code that will take the markup and add any
event listeners to it or any interactivity to it. And then we also make sure that if there is
data, we add that to the window object so that we can pick that up on the client. And then all we do is we throw in the markup
which was created up here by calling renderToString. So then once React Router picks up, that brings
us here. We render a router. Our app is going to just go ahead and map
over our route config. And the reason we need a route config is so
that the server and the client are aware of our routes. We render a few different routes. One of those is the grid component. If grid is being rendered on the server, props.staticContext.data
is going to be a thing because of this line right here. If it’s not, then it’s going to be undefined,
but we will get some data from our window object. However, if the grid component was neither
rendered on the server and there’s nothing on window, what that means is we got to this
specific route from React Router, meaning it’s just all client-side stuff. So what we do just like we normally do on
a client-side app is when the component mounts, we fetch some data, and then when any of the
URL parameters change, we go ahead and re-fetch some data and then we just show that to the
view. And that’s it. I’m going to bed.

33 thoughts on “Server Rendering with React and React Router v4

  1. N. S. Post author

    Hey man, I like all your videos. One of the best React channels! Subscribed. Keep up the good work and hope your channel will grow more! 🙂

    Reply
  2. Grigory Manusha Post author

    Extremely useful and interesting. Million thanks.

    Reply
  3. Tyler McGinnis Post author

    React v16.3 came out with plans to deprecate componentWillReceiveProps. With that said, there's only one very small part where we use that and it's at 49:11. Instead of componentWillReceiveProps, you'll use componentDidUpdate. You can see the change here – https://github.com/tylermcginnis/rrssr/commit/fb10942d736cf28ea99899af00c914a26782e5c6

    Reply
  4. Elissaios Kon Post author

    hello, thanks for the nice video. i have one question.i made pagination. At first request data are server side rendered. At click on second page i make an http request on componend did mount and all works nice.But the data are not server side rendered. Is there a way to have server side rendering on every time i fetch data both from client and server?thanks

    Reply
  5. OH SHUT IT Post author

    This was an amazing hekp. You just made SSR simple for me…

    Reply
  6. Aaron Shier Post author

    Great tutorial, very helpful during this complicated learning curve. Question, how do I deploy this?

    Edit: duh… 'npm i forever -g' then 'forever start server.js' from the root folder

    Reply
  7. Alex Nezhynsky Post author

    Very nice tut, had to weak it to get it working, but anyway, thanks!

    Reply
  8. Jakub Bińczyk Post author

    By far the best explanation i've found. Thank you.

    Reply
  9. HOUSSEM HICHRI Post author

    Yep, Helped a lot, Thanks for the tuto

    Reply
  10. Pardip Bhatti Post author

    can we use redux with this implementation ?

    Reply
  11. Austin Jess Post author

    When I run npm start, it says Cannot Find Module '…/ssr-react/index.js' and then [nodemon] app crashed – waiting for file changes before starting.

    Anyone else ran into this?

    Reply
  12. Apolo Du Post author

    Example at 18:49 is awesome !!
    Finally I understand how react cooperate on client side with markups pre-rendered in real DOM.
    BTW this is the third tutorial for React SSR I've finished, I should've watched this one first !

    Reply
  13. OH SHUT IT Post author

    Hey, thank you so much for this tutorial Tyler. I've been using a setup based on your tutorial since early May. I've noticed some issues with webpack not compiling after a while, I thought this was due to using port 80 and using sudo to run the app for long periods of time but it still has been happening with port 8000 for the last couple of months. Any thoughts or suggestions on that?

    Reply
  14. Eric Ellison Post author

    14:45 what if we had built javascript chunks ???? we are just passing bundle .js here but what if we had something like setting.bundle.js or many more chunks ????
    also how can i implement dynamic import with this ????? nice tut dude , many thanks ….

    Reply
  15. Daniel Rodriguez del Villar Trimarchi Post author

    was going to pay for the course, but then I realized it does not use webpack 4x :'(

    EDIT: Created a PR on your repo with updated dependencies 😉

    How would I add TypeScript (awesome-typescript-loader) to this setup?

    Reply
  16. Lajos György Mészáros Post author

    Here is a gist, which contains an isomorphicConnect function for all redux fans. This should be a drop in replacement for react-redux's connect function. On the backend it assumes, that all the initial data is available through the staticContext, where else on the client side it assumes, that it has been passed as the redux store's default state.
    https://gist.github.com/meszaros-lajos-gyorgy/aa012d7c80d60a3aebe127315efee6af

    Reply
  17. Tobiasz Łoś Post author

    45:12 I dont know but i dont have this error, whats is wrong ???

    @TylerMcGinnis.com

    Reply
  18. Gabe Christiansen Post author

    Great video. Very helpful! The question I now have is how do we handle nested routes with SSR? especially with a single route config file? Thanks

    Reply
  19. Max M W Post author

    I really appreciated the quick pace and thorough details. Can you recommend any reading on why I'd opt for server side rendering?
    For me the gains seems small in relation to the whole, as the API calls (at least in this example) are still made from the client.

    Reply
  20. michael osinowo Post author

    can this set up be used for production apps @TylerMcGinnis.com

    Reply
  21. Satheesh Thangavel Post author

    Thank you so much. Really helpful to understand SSR. I tried reading different articles ended up confusing myself and almost give up 😉

    Reply
  22. Z V Post author

    Hi there! I'm really new to React, but wondered why you didn't just use the 'npx create-react-app' to build out all the json, webpack etc? I'm sure you have a great reason, I'm just trying to learn

    Reply
  23. Krzysztof Dąbrowski Post author

    Thank you. That was very good.

    Reply
  24. Zain Syed Post author

    Great tutorial thanks :). Heads up – if anyone cant access the window._INITDATA_ make sure you put the script tag before the bundle in the server-rendered html so it's available when the bundle.js is loaded e.g
    <script>window._INITDATA_ = ${serialize(initData)}</script>
    <script src="bundle.js"></script>

    Reply
  25. Gjergj Kadriu Post author

    I feel like nitpicking but why this.setState(() => ({repos})) when this.setState({repos}) is the same? 46:45

    Reply
  26. Tyler McGinnis Post author

    🚀 Try our new React Hooks Course – https://tylermcginnis.com/courses/react-hooks/?utm_source=youtube&utm_medium=video&utm_campaign=why-react-hooks

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *