未加星标

Universal JavaScript Apps with React Router 4

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二03 | 时间 2017 | 作者 红领巾 ] 0人收藏点击收藏
How to use the latest version of React Router both on the server side and the client side
Universal JavaScript Apps with React Router 4

React Router is the most popular library in React-land for rendering different page contents depending on request URLs and manipulating the browser history to keep the URL displayed in the location bar in sync with your app as the user interacts with the page.

Shiny and New

Recently, version 4 of React Router entered the beta release phase. Bemoaned by some, applauded by others, it is a complete rewrite of the previous version with lots of breaking API changes.

The key idea behind version 4 is "declarative composability" - it embraces the component concept that makes React so great and applies it to routing. Every part of React Router 4 is a React component: Router , Route , Link , etc.

One of the React Router developers, Ryan Florence , made a short hands-on video introduction to the latest React Router, which I highly recommend:

What About the Backend?

The new version of React Router comes with a new web page that has lots of useful code examples . One thing I miss, however, is a practical example on how to use React Router for rendering React-based pages on the server side.

For the project I'm currently working on, search-engine friendliness and optimal site speed are essential, so rendering the whole page on the client side - the way all the examples on the examples page do - is not feasible. We use an Express server to render our React pages in the backend.

In his intro video, Ryan has an App component that fetches data from some API to initialize its state, using the componentDidMount lifecycle method. When the asychronous data fetching is done, the component is updated to display that data.

But this doesn't work when rendering the App component on the server side: when you use renderToString , the string with HTML code is created synchronously, after calling the component's render method once. componentDidMount is never called.

So if we rendered the App component from Ryan's video example in the backend, it will just generate the "Loading..." message.

I struggled with this for some time and complained about it on Twitter:

Thankfully, Ryan replied to my tweet and pointed me in the right direction:

The Solution

As a proof of concept, I created a demo app that basically recreates Ryan's example from the video using server-side rendering.

The app fetches data about Gist code snippets using the GitHub API :


Universal JavaScript Apps with React Router 4
Show Me the Code!

You can find the demo app's source code here on GitHub:

In a nutshell, here's what I did...

server/index.js

This is the code that is run with every HTTP request to the Express server:

(Note: this is just an excerpt - you can find the full source code on GitHub )

In lines 1-4 , I'm defining an array of routes for my app. The first one is for initial requests for the main page, without any Gists selected. The second route is for displaying a selected Gist.

In line 6 , my Express app is told to handle any request that comes in using an asterisk match.

In line 7 , I'm reducing my routes array using the matchPath function from React Router; the result is a match object with information about the matching route and any parameters that may be parsed from the URL path.

In lines 8-11 , if there is no matching route, I'm rendering an error page that says: "Page not found".

The render function here is just a wrapper around React's renderToString that adds the basic page HTML code around the React component's HTML ( <html> , <head> , <body> , etc.).

In lines 12-22 , I'm fetching the data to populate my App's state from the GitHub API and rendering my App component.

Most notably, in line 17 I'm using the StaticRouter component to initialize React Router. This Router component type is the best choice for server-side rendering. It never changes its location, which is what we want in this case, since on the backend, we are just rendering once and not directly reacting to user interations.

Line 23 catches any errors that accur during the process to render an error page, instead.

App.js

My App component looks like this:

In line 1 , the component receives the gists data object as a prop.

Lines 3-13 render a Sidebar component with links to the various Gists. The SidebarItem components contained within are only rendered if there is actually gist data available. On the server, this is always the case. We are, however, using this component for both server-side and client-side rendering. If the component is rendered in the client, we may be in the process of fetching fresh gist data, so we display a "Loading..." message instead.

Line 15 uses a Route component from the React Router library to display the Home component when the route matches the path "/". We are using an exact match here, otherwise any path that simply starts with a slash would match.

If there is some gist data to display, in line 18 , another Route component is used to display a Gist component with details about a selected gist.

client/index.js

As mentioned above, this is a universal javascript application (f.k.a. "isomorphic"), meaning the same code is used to render pages on the server and on the client. Here is an excerpt from the code that initializes the page on the client side:

Much simpler than the server-side version! The render function in line 1 is just the render function of ReactDOM . It attaches the layout rendered by my React components to a DOM node.

In line 2 , I'm now using the BrowserRouter (instead of the StaticRouter I used for server-side rendering).

Instead of fetching the initial data from the GitHub API, in line 3 I'm instantiating my App component with gist data from a global variable in the browser DOM, which the backend put there via a <script> tag.

That's basically it!

When I open up my app in the browser, I can click on any of the Gists in the sidebar. The client-side React Router makes sure that with each click on a link, the page's URL is updated and the parts of the page dependent on the new URL are re-rendered. When I hit the browser's reload button, the backend's static router makes sure that the same page with the correct data is displayed.

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

主题: ReactJavaScriptJavaGitHubGitHTMLTwitter
分页:12
转载请注明
本文标题:Universal JavaScript Apps with React Router 4
本站链接:http://www.codesec.net/view/534842.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(14)