I'm using server side rendering for my React app but can't wrap my head around the logic for showing error pages when something goes wrong.
For example:
User requests /article/123
Something goes wrong while fetching the article
I would like to show a 500 error page
The server side part was easy. I tell React-Router to serve my error component. So far so good.
But when the page is served, the client-side javascript is executed and it takes over rendering. React-Router see the url /article/123 and loads the component that shows the article (but fails since the data is not present..)
Is there a way to let the client-side know that we want to show the error component instead?
The only think I could think of is the following: Add the error to the global redux state. Before rendering a component, check if the error is present in the global state and show the error component instead.. But the downside of this is that you have to implement that checking logic in all of your components. There should be some kind more elegant way to fix this..
There's a few different ways to implement client-side error handling; I find using Higher Order Components work best. You would create a wrapper component that checks for errors from the server response. If it finds one, serve the appropriate error page. If the HOC doesn't detect an error, serve the component the user originally requested.
Here's a great explanation on how to implement HOC:
https://medium.com/#franleplant/react-higher-order-components-in-depth-cf9032ee6c3e
Related
I currently have a React app that I'd like to use SSR with. All but one component is pretty much static content, making SSR super easy. Everything but the component is rendered just fine and well right now.
My question is how do I go about rendering this component that needs to first get data? Because it's a complex SVG that gets rendered my line of thinking is that having it "update" once data comes in is a bad move, and it would be better for it to just not exist in the absence of data (with an error message).
So here's my plan: I can add a prop to the component to pass in data from a parent rather than keep it as internal state only. So if data is passed, no fetch request in the component is necessary. From there what I can do is take the static bundle output of the app and, when the page is requested, the server will request the proper data just as the component would. Once the data is received, the server can grab the component from the bundle with regex, add the data as a prop, render the component, and stick it back in with the rest of the already rendered static content.
Is this the correct way to do this? It feels a bit complicated, but that might just be how it's done. I'm not sure.
Your intuitions are correct. In current React (17.0), it's quite cumbersome to do SSR with data-fetching inside components.
What needs to be achieved on a conceptual level is that all data dependencies need to be known upfront, ie. before calling ReactDOM's render. This way, one can access the data in a synchronous manner which allows to do one-pass render on a server.
I don't quite follow your idea to "grap the component from the bundle with regex". One way of solving the data dependency problem is to inject the data into React tree from the root component (ie. ReactDOM.renderToString(<App componentStaticData={data} />)) and make the data-dependent component aware of the fact that it can just grab the data from there instead of doing (asynchronous) call. It's important to note that useEffects are not executed on the server.
Another idea to grab all the data dependencies is to do two-pass render. First one is used as a way to collect all resources used, then we await their completion and inject them as static data into send pass.
Third way is to use one of the React frameworks that provide SSR out of the box. You can have a look at (among many others) Next.js or Gatsby. Depending on your setup, this might be easiest or the hardest way to achieve SSR.
If a breaking error occurs in a React component, the whole page crashes. For instance, if x is null and you try to access x.someProperty, you'll get an error and the whole page will go white. This is a little bit different from an old-style app that isn't running entirely on JS, because the markup (HTML&CSS) would still be there even if the JS errored out and blocked some aspects of the page.
Is there a way to mitigate this with React? Something like wrapping a component in a try/catch so that if something goes wrong, only that component fails and only that part of the page goes white, rather than the entire page. I'm not sure if there's a better pattern than literally wrapping the entire body of a functional component's code in a try/catch.
I suppose I'm particularly interested in functional components here, but a class-based answer might be useful for someone else.
You can mitigate such errors using Error Boundaries - such components may catch errors thrown from the child components and display some meaningful error instead of just crashing.
I've just implemented React Router 4 in my React/Redux app which was using multiple entry points up to this point.
I changed my code to have a single entry point and set up a catch-all route on the server side so that react router would handle requests on the client side.
Initially, everything seems to work OK but it takes a long long time before the right component is displayed. As soon as I click the link, I see the URL in the browser address bar change immediately but I have to wait sometimes more than 10-15 seconds before the right component is loaded.
I've also noticed that after a click or two, it stops working all together.
I don't get any errors and I put a break point on the server to see if I'm somehow hitting the backend when I click a link but looks like I'm not even going to the backend.
How can I debug/troubleshoot this? React Router is just a bunch of components and there's not much code to speak of that I can debug.
Has anyone experienced this issue? I'd appreciate any ideas you may have. Thanks.
The answer was in this post: React Router v4 not rendering components
Because I'm using Redux, I needed to use withRouter.
I created server side rendering with approach described in redux's official site, everything is great, but on client side it rendering components again, and this is not good I think. I am passing same state from server to window.__STATE__ variable and passing this to my client side createStore method as initial state, but it rerendering again.
Also please write in comments which part of code is needed to you, if so.
I am not providing since it is very similar to official page instructions code and there is no errors, just issue with rerendering, but as I understand it is not connecting to virtual DOM.
Please help me find valid way for handling this task.
Take a look at this example from the ReactGo project: https://github.com/reactGo/reactGo/blob/master/app/client.jsx#L22
They use a function onUpdate that has the conditional
if (window.__INITIAL_STATE__ !== null) {
window.__INITIAL_STATE__ = null;
return;
}
which prevents a duplicate fetches if __INITIAL_STATE__ is already defined. Your components rerendering may have something to do with the duplicate fetching.
Perhaps I am not understanding what you mean by re-rendering, but it is supposed to "re-render" on the client again. The way isomorphic works is that it renders the HTML on the server, and then the payload includes the initial state as well has the HTML markup - this way the browser "appears" to have faster page load times - since the UI is rendered even before the script is executed. Now once the HTML parsed and the script runs, React internally builds the virtual DOM and then compares it to the server generated DOM and wires up event listeners etc. It does not however, do a full re-render in that no new DOM elements should be created. If for any reason the client run of your React render results in a virtual DOM that is different from the generated server DOM, React will give you a warning.
"Warning: React attempted to reuse markup in a container but the
checksum was invalid. This generally means that you are using server
rendering and the markup generated on the server was not what the
client was expecting. React injected new markup to compensate which
works but you have lost many of the benefits of server rendering.
Instead, figure out why the markup being generated is different on the
client or server:"
I have window dependency in some of modules I use, so I cannot render part of my app on a server. However the content send from a server should be the same as a content rendered on a client first time, otherwise there will be a: "React attempted to use reuse markup in a container but the checksum was invalid" warning.
Seems like I need to do some workaround, render the content from a server and then, after component is mounted, render a client only part. Similar issue described here.
I feel this is the best solution, however eslint from AirBnB throws an error that changing state in componentDiMount is not good.
Do you have better ideas?