A lot of components are re-rendering with every single operation - reactjs

The performance of my React app is very poor and if you select "Highlight updates" in Chrome you'll find basically every single component re-renders with every move you make.
Open a simple modal? Everything in the app re-render, right down to the FontAwesome icons.
Click anywhere on the screen? Everything re-renders.
I've added really aggressive shouldComponentUpdates to basically every component.
I've used DeepDiff to check what's causing the updates (nothing is. The props/state are basically always the same).
What else can I check to try and solve this? There's nothing fancy at all going on, and most of the time it doesn't even involve Redux or anything like that.

Related

React asynchronous state update causing unresponsive User Experience

My top-level React component provides a context to all its children which is used to render UI. The context is an array, and a UI element is generated for each element in the array. Users can delete items from this array using buttons placed around the app and throughout the component tree.
The problem is when an element is deleted, the context takes a really long time to update and the app feels unresponsive. I know it's because it's async, but it's causing UX issues.
How can I make my app more responsive? Is there a way to force a state update?
I'm not sure how to approach fixing this. Would a state management lib work?
Turns out this is an issue with Chrome being a memory hog. I cleared caches and cookies, restarted chrome + server, and everything started working. What a silly thing to spend hours profiling your react app for! Hopefully this can help someone else.

Links very slow to display 'large' components

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.

How to cache or prevent re-render of large reagent component

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.

How to check when the react components get rendered or rerendered

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.

React internals: How does React know how to rerender an iframe correctly?

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.

Resources