I have a reagent single page application. At the root component, I have a route dispatcher that renders component according to route. Something like:
(defn page []
[(get component-lookup #current-page-name)])
I have a list page and a detail page, where user frequently switches back and forth.
Problem is, switching between these two pages destroys and re-create an entire page worth of components. This slows down page transition noticeably, especially in mobile devices. In contrast, switching from one detail page to another is very fast, as react figured only a few components need to be re-rendered.
My first attempt at optimization is to have both pages rendered all the time, but add display:none whenever they are not visible. This speeds up page transition, but changes the meaning of lifecycle methods. For example, component-did-mount would execute, but the component is not visible and I cannot update its scroll position.
Would there be a better way to do this? Maybe it is possible to unmount a component tree, save it along with its subcomponents and states/virtual dom, to be remounted later with new props?
I don't think you're going to be able to do this without forking react. The lifecycles are inherently tied to modifications to the virtual dom--that's their whole point of existence.
It's a little surprising that the mere rendering of the DOM elements are too slow, unless your page has thousands of dom elements or lots of media. What are you doing during mount? If it is compute intensive, I'd look there to see if you can separate out that logic and cache it so that it is a lightweight process to switch components.
Related
We are migrating our web application from React to SolidJS. We want to make the change gradually by converting pages one at a time.
I have been using astro to run both react and solidjs.
The problem I encountered was navigating between react pages and solidjs pages, as they use different routers (react-router-dom and solid/router).
How can I navigate between these two UI libraries?
React uses virtual DOM and re-renders whole DOM tree with each state update. To reduce the work, it diffs and batches previously rendered branches.
Solid renders components into actual DOM elements and there is diffing or batching but some built-in methods/components keep object references to reduce re-calculations if related state branch is unchanged, i.e mapArray, indexArray, For and Index. It is closer to caching than diffing and batching.
So, mixing and matching at component level will be hard to reason about and certainly will be sub-optimal in terms of performance. I believe the best option is to split the work at the root level and migrate each root level components as a whole.
To make it more concrete, lets say you have three paths in your router:
/home
/contact
/about
So, you will have three root level components:
Home
Contact
About
Migrating each of them one by one will be cleaner and more performant.
However, if your application is complicated or hard to split at root level or if you don't want to maintain multiple routers, then use Solid-js for rendering the root level components, because those will be actual DOM elements, and mount the React components carrying the old logic on top of them. So you will have have multiple ReactDOM.render calls mounting legacy components on the DOM elements initially rendered by solid-router. I think that will be the best course of action.
I am working on a React app, and within it there is a page with a lot of graphs and large list component, which takes some time to load (performance dependant). This is fine for now, but the issue comes from the following:
at the first render, there is an API call (App wide as different pages use the same data). A loading spinner shows while the API data is fetched
Once the data is fetched, it is Redux manages the state, and each component just takes what it needs from there (no more loading).
My issue is that when I navigate between pages trough links (react-router), after I click on the link it takes a while for the page to show and the menu to update the current page. None of the state data has changed in that timeframe, so I assumed a PureComponent would prevent re-render, but it doesn't work.
Is there any way to not re-render the page? I would expect to click on a link an immediately see the page that was already loaded before.
Sorry if the question is obvious, I got quite confused while researching this, with cold splitting, lazy loading, etc. which seems to be more about initial load vs. navigation?
If you have a large component dataset to mount and your state does not changes or affects re-renders you could prevent component from unmounting entirely.
If you are using react-router you can rely on setRouteLeaveHook.
If your unmount depends on conditional rendering, it is easier as you can hide your component in various way, including css display:none
There are several ways you can do this
The first one would be to not unmount the component, just hide it with CSS and display: none, but that's a shit solution if you ask me, since you'll still have the component in the DOM.
Another solution that you can use is the one that the guys from the Facebook team used when creating the official website for React. Once you hover over the link to the page, send a request to prefetch the data for that page. So, before the user even clicked, you will have the request already sent.
Another solution you can use is to cache the data. If you are not worried about users with older browsers waiting a bit for the component to load again. You can keep the data for the component in localStorage, then, when you are about to mount the component, check if the data is already in localStorage if it's there, just render the component, if not, just grab the data again.
A combination of the first and the second option would make your component pretty much always instantly render.
Is there any way to get logs of which all React components get rendered or rerendered when any event/action is performed. I just want to check whether any unwanted React component is getting rerendered. I know we could achieve this by adding consoles in render function, componentWillReceiveProps or componentWillUpdate. But I have number of components in my webapp. Adding console statements in each component would be a mess. Is there a better way?
A nice option is to visualise React components in the chrome timeline. This lets you see which components exactly get mounted, updated, and unmounted and how much time they take relative to each other. That feature was added as part of the release of React 15.4.0. You can take a look at the official blogpost to get a better insight, but summing up, those are the steps to get it up and running:
Load your app with ?react_perf in the query string (for example, http://localhost:3000/?react_perf).
Open the Chrome DevTools Timeline tab and press Record.
Perform the actions you want to profile. Don't record more than 20 seconds or Chrome might hang.
Stop recording.
React events will be grouped under the User Timing label.
After that, you will get a cool visual representation of mounting, updating and unmounting of your different components over time
In addition to rauliyohmc answer. Profiling tool he described is really helpful and powerful, but if your task is just checking if there are unnecessary re-renders (i.e. ones which did not lead to DOM changes) you can use nice and simple react-addons-perf tool. It has printWasted method which will provide you info about wasted re-renders.
I was experimenting with React and rerendering iframes and I was not sure how React correctly rerenders iframes, especially ones which point to a text editor. Here is a jsFiddle showing this:
https://jsfiddle.net/augburto/fkqnm329/2/
The text editor I point to is not important but what I am doing is when you click Trigger Update, it will call a setInterval which will constantly set a new state and thus trigger a rerender.
What I thought might happen is that when I'm typing in the iframe which has a textarea, it will inevitably rerender and thus make me lose my text editor position but somehow I am able to type seamlessly without any issues despite seeing the rerenders come through in the console.log. Note I'm not suggesting it should do this -- I'm just wondering why it doesn't do that. I know React internals do some smart things like transactions but I wouldn't expect it to maintain my cursor position or what I have selected.
So how does React handle rerendering iframes more specifically? I have read articles (i.e. this and this but they don't shed a lot of light on it I feel). Is it any different from regular DOM elements?
Okay so after some time, I think I believe I discovered the answer. In general, because React just creates a virtual DOM and does a diff behind the scenes, it really is only looking at the iframe element and not what is generated. That is a completely separate thing where the iframe creates its own browsing context. Reading the MDN documentation on iframe was really informative:
The HTML element represents a nested browsing context, effectively embedding another HTML page into the current page. In HTML 4.01, a document may contain a head and a body or a head and a frameset, but not both a body and a frameset. However, an can be used within a normal document body. Each browsing context has its own session history and active document. The browsing context that contains the embedded content is called the parent browsing context. The top-level browsing context (which has no parent) is typically the browser window.
I think React does do some special things when it comes to parsing an iframe but overall it doesn't deviate too much in general.
Also something that I think might be informative is how React handles input elements in general -- take a look at Controlled Components in general for Forms as React has a completely separate and special way of handling those cases.
React relies on the data-react-checksum attribute for reusing markup, which is only set in renderComponentToString (used for server-side rendering of components).
How can I reuse the react-component markup when the component is rendered client-side only?
Background
I'm working on a client-side only webapp. One of the project's goals is to render as quickly as possible, reducing UI "lag" from uninitialized elements, JS loading/parsing, and so forth.
I'm want to cache the rendered react-components markup using localStorage so the previous UI state can be restored as early as possible (again for performance reasons without waiting ~200ms for react to be loaded and parsed).
Strictly speaking, there's no way to turn a string of markup into a live React component (at least not in a performant way). Plus, it doesn't really make sense as your component might have had hidden state that aren't shown on the final rendered DOM string. If you try to revert the string into a component, it'll lead to state inconsistencies.
Something neat to do is to serialize your data into localStorage, and then reproduce your components from that data. But this more or less helps in your case.
But still, you can roll your own small logic on putting the string markup, then letting React destroy it subsequently by rendering a second time.