Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance Checklist #77

Open
76 tasks
coryhouse opened this issue Nov 5, 2021 · 0 comments
Open
76 tasks

Performance Checklist #77

coryhouse opened this issue Nov 5, 2021 · 0 comments

Comments

@coryhouse
Copy link
Owner

coryhouse commented Nov 5, 2021

42+ ways to make your React app faster ⚛️:

Performance Testing

Generate a simple fake dataset in a loop.

export function generateProducts() {
  const products = [];
  for (let i = 0; i < 10000; i++) {
    products.push(`Product ${i+1}`);
  }
  return products;
}

Generate a large fake dataset using tools like Faker, Chance, etc.

import faker from "faker";

export const fakeNames = Array.from(Array(10000), () => {
  return faker.name.findName();
});

Simulate a slow component via a loop:

function ExpensiveTree() {
  let now = performance.now();
  while (performance.now() - now < 100) {
    // Artificial delay -- do nothing for 100ms
  }
  return <p>I am a very slow component tree.</p>;
}

Forms

HTTP

Rendering

  • Statically render if possible (Easy via Next.js, Gatsby)
  • Lift fetches to common parent or fetch in parallel via tools consider server rendering to avoid slow network waterfalls (easy via Remix / Next.js)
  • Prefetch / render-as-you-fetch (React's "default" is fetch as you render since the render triggers a fetch via useEffect). React-query's prefetching is a simple way to render as you fetch. Remix also does this by default since nested routes declare data dependencies and run in parallel on the server. My tweet on this
  • If server rendering with Remix, Next, Docusaurus, etc, Enable time-slicing with React.startTransition at the root. This avoids jank if the user scrolls before hydration completes.
  • Consider using million's block virtual DOM instead if there is a lot of static content with little dynamic content. Million diffs state instead of DOM, which is fast if there's a little state, but a LOT of DOM.

Context

Routing

Keys

  • Assure keys are assigned, and stable over time. Their values should not change based on array order or edits to the data.
  • Reuse keys to avoid needless renders for items that frequently appear/disappear in the viewport.
  • Consider using the array’s index as key for dynamic lists with stateless items, where items are replaced with the new ones - paginated lists, search and autocomplete results and the like. This will improve the list’s performance because the entire thing doesn't have to re-render. The component instances are reused. Demo and blog post

State

  • Keep state as local as possible. Start by declaring state in the component that uses it. Lift as needed.
  • Store data that doesn't need to render in refs
  • Consider useReducer over useState so you can pass dispatch down instead of callbacks (avoids needless renders)
  • Avoid deep cloning. To avoid, only clone the subobjects that have changed. Or perhaps better yet, avoid nesting objects in state since doing so can lead to needless renders. Instead, "flatten" state by creating separate pieces of state.
  • Use memo, ref, or pass a func to useState if the initial value calls an expensive func, otherwise, it will be calculated on each render.
  • Use useTransition for low priority updates (reduces jank) - Demo
  • Use useLayoutEffect to avoid a Flash of unstyled content when you need to read and manipulate the DOM before the user sees it - Demo

Memoization / Identity

Props

  • Pass the minimal amount of data to each component (remember, components re-render when props change)
  • Pass primitives (strings, numbers...) to child components to assist with diffing. Avoid passing arrow funcs and objects on props when performance is a concern since it leads to needless re-renders of child components.
  • useDeferredValue if it's a low priority update and you can't use useTransition because you don't control the value coming in (coming from third party or via a prop you can't control). Another demo

Component composition / Children

  • Put content that renders frequently in a separate component to minimize the amount that's rendered
  • Embrace reusable components. Each reuse of a reusable component is nearly free.
  • Compose higher level components out of lower level components.
  • Create layout components to centralize reusable layout
  • Lift content up - Pass children down (do this before you memo). Why does this work? Because components passed as children don’t re-render since they are just props. Note: don't pass a function as a child, since it'll still re-render because the func will be recreated on each render
  • Declare functions that need not be in the React component outside the component. This way they're not reallocated on every render.
  • Declare static values outside the component so they're not reallocated on every render.

Design

  • Use pagination, sorting, filtering, pages, tabs, accordions, modals, etc to avoid displaying too much data at the same time.
  • Use tools like react-window to handle large lists by only rendering what's currently visible.
  • Consider implementing optimistic updates when the API is slow (immediately update the UI even though the API call is still in progress behind the scenes)

Bundle optimization

Third party libraries

  • Avoid named imports when importing third party libraries / components (doing so can bloat the bundle by importing the entire lib) For example, avoid import { Tab } from "x". Prefer import Tab from "x/Tab" when possible.
  • Prefer native HTML inputs over fancy components that simulate native behaviors
  • Use Partytown to load heavy third party libraries via a separate web worker thread

Styling

  • Consider Tailwind to minimize style bundle size
  • Consider CSS modules over CSS-in-JS to avoid a runtime

Framework

  • Consider Gatsby or Astrobuild to compile components into static HTML
  • Consider Next.js, Remix, Redwood to server render

More examples

https://piyushsinha.tech/optimizing-performance-in-react-apps-i
https://piyushsinha.tech/optimizing-performance-in-react-apps-ii

This was referenced May 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant