React Performance Optimization Through Composition

Oscar Bustos

Learn how to improve React app performance using composition patterns instead of complex optimizations

6 min read

When dealing with performance issues in React, developers often reach for complex solutions like useMemo, useCallback, or React.memo. However, simple composition patterns can often solve performance problems more elegantly.

The Problem: Unnecessary Re-renders

Consider this common scenario:

const App = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);
  
  return (
    <div>
      <ExpensiveComponent />
      <ComplexDataGrid />
      <button onClick={() => setIsModalOpen(true)}>
        Open Modal
      </button>
      {isModalOpen && <Modal onClose={() => setIsModalOpen(false)} />}
    </div>
  );
}

Every time the modal opens or closes, both ExpensiveComponent and ComplexDataGrid will re-render unnecessarily. This happens because state changes trigger re-renders for the component and all its children.

Solution 1: Moving State Down

Instead of managing modal state in the parent, we can isolate it in its own component:

// ✅ Good: Modal state is isolated
const ModalButton = () => {
  const [isOpen, setIsOpen] = useState(false);
  
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Open Modal
      </button>
      {isOpen && <Modal onClose={() => setIsOpen(false)} />}
    </>
  );
}

// Now the expensive components won't re-render when modal state changes
const App = () => {
  return (
    <div>
      <ExpensiveComponent />
      <ComplexDataGrid />
      <ModalButton />
    </div>
  );
}

Solution 2: Children as Props

Sometimes you need to share data between components but don’t want to trigger unnecessary re-renders. The children prop is perfect for this:

const Layout = ({ children }) => {
  const [sidebarWidth, setSidebarWidth] = useState(200);
  
  return (
    <div style={{ display: 'flex' }}>
      <Sidebar width={sidebarWidth} onResize={setSidebarWidth} />
      <main>
        {children}
      </main>
    </div>
  );
}

// Changes to sidebar width won't cause content to re-render
const App = () => {
  return (
    <Layout>
      <ExpensiveComponent />
      <ComplexDataGrid />
    </Layout>
  );
}

Solution 3: Component Splitting

When dealing with forms, split components based on which parts need to update together:

const ExpensivePreview = ({ data }) => {
  // Heavy calculations here
  return <div>{/* Complex rendering */}</div>;
}

const Form = () => {
  const [formData, setFormData] = useState(initialData);
  
  return (
    <div>
      <FormInputs 
        data={formData}
        onChange={setFormData}
      />
      <ExpensivePreview data={formData} />
    </div>
  );
}

// Only form inputs re-render on every keystroke
const FormInputs = ({ data, onChange }) => {
  return (
    <div>
      <input 
        value={data.name}
        onChange={e => onChange({ ...data, name: e.target.value })}
      />
      {/* More inputs */}
    </div>
  );
}

Real World Example: DataGrid with Filters

Here’s a practical example combining these patterns:

const DataGridWithFilters = () => {
  return (
    <div>
      <FiltersSection />
      <DataGrid />
    </div>
  );
}

const FiltersSection = () => {
  const [filters, setFilters] = useState(initialFilters);
  
  return (
    <FilterContext.Provider value={{ filters, setFilters }}>
      <div>
        <QuickFilters />
        <AdvancedFilters />
      </div>
    </FilterContext.Provider>
  );
}

const DataGrid = () => {
  const { filters } = useFilterContext();
  
  return (
    <div>
      {/* Complex grid rendering */}
    </div>
  );
}

This structure ensures that:

  • Filter changes only re-render filter components
  • The grid only re-renders when filters actually change
  • No need for complex memoization

Key Benefits

  1. More maintainable code
  2. Better performance by default
  3. Less risk of memoization-related bugs
  4. Clearer data flow
  5. Easier testing

Composition patterns often eliminate the need for complex optimization techniques while making your code more maintainable.

Is it a match?

If you need help or advice, feel free to drop us a message via social media or email

Contact me