I'm working on a create-react-app (v2.1.8 at time of writing, shipping with react-router-dom v4.3.1) web application, and I need help understanding why it works the way it does.
My initial problem relates to the app being served by AWS S3, and is addressed by SO questions react router doesn't work in aws s3 bucket and React-router urls don't work when refreshing or writing manually:
- My browser got 404s in my app, when trying to fetch server-side routes. This was solved by specifying pointing S3
error.html
to my app'sindex.html
... - ...but it solved the problem only halfway, as asking for
/whatever/subresource
to S3 will indeed return theindex.html
(thanks to 1. above) but will cause S3 to return a 403. My app being behind CloudFlare, I worked around following CloudFlare hack Override response status code from 404 to 200 for S3 SPA . Basically the CloudFlare worker intercepts requests, and if the requested URL matchesCRA_APP_ROUTES = ['/route-a', '/route-b', '...']
, I return the response (which isindex.html
because of S3 config described in 1.), but with a200
response.
Here's my question:
- I get that CRA uses React Router's
browserHistory
(which behind the scenes uses the HTML5pushState
history API) to get nice-looking URLs in the Single-Page App, without#/...
. - I get that the
pushState
history API will modify the history / URL without doing any navigation. As documented by MDN / Adding and modifying history,Suppose
http://mozilla.org/foo.html
executes the following JavaScript:var stateObj = { foo: "bar" }; history.pushState(stateObj, "page 2", "bar.html");
This will cause the URL bar to display
http://mozilla.org/bar.html
, but won't cause the browser to loadbar.html
or even check thatbar.html
exists. - And I get that the app "works" when served by a properly-configured Express serving
index.html
for any unknown paths. As documented by Serving Apps with Client-Side Routing,The server needs to be configured to respond to a request to
/todos/42
by servingindex.html
. For example, we can amend our Express example above to serveindex.html
for any unknown paths:app.use(express.static(path.join(__dirname, 'build'))); -app.get('/', function (req, res) { +app.get('/*', function (req, res) { res.sendFile(path.join(__dirname, 'build', 'index.html')); });
- I get that things then work because React Router recognizes the route it's at, and renders things appropriately.
But why in the first place make the network request at all? Why fetch resources that all land to index.html
& associated JS, which my browser already has?! Can a CRA be configured to A. get nice browserHistory
routing, and B. be fully client-side and not make network requests when performing client-side routing & re-rendering? (Which is wasteful since S3 isn't doing any Server-Side Rendering, and will return data the client already has). Am I missing anything?
Thanks.