Frontend Development 14 min read

Detecting and Optimizing Repeated Rendering in Large React Applications with React Scan

The article explains how repeated rendering slows large React applications, introduces the React Scan tool for automatically detecting unnecessary renders, details its installation via script tag or npm, describes its core APIs, and shows how to combine it with memoization, useCallback, useMemo, and other optimization techniques to improve performance.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Detecting and Optimizing Repeated Rendering in Large React Applications with React Scan

When building large React applications, performance problems often arise due to repeated rendering. Repeated rendering is a major bottleneck, especially before the React Compiler became widely adopted. React Scan is an automatic detection tool that highlights rendering issues, allowing developers to precisely locate components that need fixing. Learning Objectives Repeated Rendering Issues : Understand the various cases of repeated rendering in React and their impact on performance. React Scan Tool : Master the installation and usage of React Scan, both via a script tag and npm. API Usage : Familiarize with the main APIs such as scan , withScan , getReport and setOptions . Performance Optimization Techniques : Learn to use React.memo , useCallback , useMemo , shouldComponentUpdate and PureComponent to improve React app performance. Practical Application : Apply the tool in a real project with example code. React Repeated Rendering React monitors component state and props to decide when a component should re‑render. Changes trigger re‑rendering of the component and its children, which can lead to unnecessary work. 1. Reference Types Trigger Re‑render React performs a shallow comparison on objects and arrays. Even if the content is unchanged, a new reference forces a re‑render. Example: // Example causing `ExpensiveComponent` to re‑render frequently alert('hi')} style={{ color: 'purple' }} /> In the code above, onClick and style are recreated on each render, causing ExpensiveComponent to re‑render even when its visual output does not change. 2. Unnecessary Parent‑to‑Child Updates A parent’s state change can cause child components to re‑render even if the child does not depend on that state. Example: function ParentComponent() { const [count, setCount] = useState(0); return ( setCount(count + 1)}>Increase Count ); } function ChildComponent() { // Child does not use `count` but still re‑renders each time `count` changes return Child Component ; } 3. Frequent Internal State Changes Components that update internal state very often cause their own frequent re‑renders. Example: function Counter() { const [count, setCount] = useState(0); useEffect(() => { const interval = setInterval(() => { setCount(prev => prev + 1); }, 1000); return () => clearInterval(interval); }, []); return Count: {count} ; } Installation You can install React Scan in two ways: Via Script Tag Insert the following script before any other scripts: <!doctype html> <html lang="en"> <head> <script src="https://unpkg.com/react-scan/dist/auto.global.js"></script> </head> <body> </body> </html> For Next.js, add the script to pages/_document.tsx : import { Html, Head, Main, NextScript } from 'next/document'; export default function Document() { return ( {/* other scripts */} ); } Via npm If you prefer package management, run: npm install react-scan Then import before React: import { scan } from 'react-scan'; // import before React import React from 'react'; scan({ enabled: true, log: true, playSound: true, showToolbar: true, report: true, }); API Reference scan(options) Automatically scans the application for renders. scan({ enabled: true, // enable/disable scanning includeChildren: true, // include children of components wrapped with withScan playSound: true, // toggle sound effect log: false, // log render info to console showToolbar: true, // display toolbar UI renderCountThreshold: 0, // only show components rendered more than this count report: false, // report data to getReport() onCommitStart: () => {}, onRender: (fiber, render) => {}, onCommitFinish: () => {}, onPaintStart: (outline) => {}, onPaintFinish: (outline) => {}, }); withScan(Component, options) Scans a specific component. function Component(props) { // ... } withScan(Component, { enabled: true, includeChildren: true, playSound: true, log: false, showToolbar: true, renderCountThreshold: 0, report: false, onCommitStart: () => {}, onRender: (fiber, render) => {}, onCommitFinish: () => {}, onPaintStart: (outline) => {}, onPaintFinish: (outline) => {}, }); getReport() Retrieves a summary report of all components and their render statistics. scan({ report: true }); const report = getReport(); for (const component in report) { const { count, time } = report[component]; console.log(`${component} rendered ${count} times, took ${time}ms`); } setOptions(options) & getOptions() Adjust or retrieve the current scanning configuration. setOptions({ enabled: true, includeChildren: true, playSound: true }); const options = getOptions(); console.log(options); Demo A complete demo that integrates React Scan with several components: import React, { useState } from 'react'; import type { FC } from 'react'; import { scan, getReport } from 'react-scan'; import ExpensiveComponent from './components/ExpensiveComponent'; import Counter from './components/Counter'; import ParentComponent from './components/ParentComponent'; // Initialize React Scan scan({ enabled: true, log: true, playSound: true, showToolbar: true, report: true, }); setInterval(() => { const report = getReport(); for (const component in report) { const { count, time } = report[component]; console.log(`${component} rendered ${count} times, took ${time}ms`); } }, 1000); const App: FC = () => { const [theme, setTheme] = useState<'light' | 'dark'>('light'); return ( React Scan Demo setTheme(theme === 'light' ? 'dark' : 'light')} style={{ marginBottom: '20px' }} > 切换主题 alert('clicked')} style={{ color: 'purple' }} /> ); }; export default App; Switching the theme lets you see which child components re‑render and view a full performance report. FAQ Why choose this over React DevTools? React DevTools does not clearly distinguish necessary from unnecessary renders and lacks a programmable API. React Scan provides explicit render counts and a toolbar. Does it support React Native? Support is coming soon. Is there a Chrome extension? It will be released shortly. General React Performance Solutions 1. Use React.memo React.memo is a higher‑order component that caches the rendered output of a functional component when its props have not changed. const ChildComponent = React.memo(function ChildComponent() { return Child Component ; }); 2. Use useCallback and useMemo These hooks cache functions and computed values to avoid recreating them on each render. function ParentComponent() { const [count, setCount] = useState(0); const handleClick = useCallback(() => { alert('hi'); }, []); const style = useMemo(() => ({ color: 'purple' }), []); return ; } 3. Use shouldComponentUpdate or PureComponent For class components, shouldComponentUpdate lets you control update logic, while React.PureComponent implements a shallow prop and state comparison automatically. class ChildComponent extends React.PureComponent { render() { return Child Component ; } }

performanceFront-endoptimizationJavaScriptReactreact-scan
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.