Using arrow functions inside JSX without memoization leading to re-renders

Loading

The Problem: Arrow Functions in JSX Cause Unnecessary Re-renders

When you define an arrow function directly inside JSX (like in onClick, onChange, etc.), a new function is created on every render. This can lead to:

  • Unnecessary child component re-renders (if the child is optimized with React.memo or shouldComponentUpdate).
  • Performance issues in large apps or frequent updates.

Bad Example (Causes Re-renders)

function MyComponent() {
  return (
    <button onClick={() => console.log("Clicked!")}> 
      Click Me 
    </button>
  );
}

Problem: A new arrow function is created every time MyComponent renders, causing <button> to re-render unnecessarily.


Solutions to Avoid Re-renders

1. Move Handler Outside JSX (Best for Simple Cases)

function MyComponent() {
  const handleClick = () => console.log("Clicked!");

  return <button onClick={handleClick}>Click Me</button>;
}

Why it works: handleClick is created once per component instance, so it doesn’t change between renders.


2. useCallback for Dependencies (Best for Dynamic Handlers)

If your handler depends on props or state, memoize it with useCallback:

function MyComponent({ userId }) {
  const handleClick = useCallback(() => {
    console.log("User ID:", userId);
  }, [userId]); // Only recreates if `userId` changes

  return <button onClick={handleClick}>Click Me</button>;
}

Why it works:

  • useCallback memoizes the function and only recreates it when userId changes.
  • Prevents unnecessary re-renders in optimized child components.

3. For Class Components: Bind in Constructor or Use Arrow Class Property

class MyComponent extends React.Component {
  handleClick = () => console.log("Clicked!"); // Arrow function (auto-bound)

  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}

Why it works:

  • The arrow function is created once per component instance (like useCallback in functional components).

When Does It Actually Matter?

  • Child components using React.memo:
    If <Button> is memoized, passing a new function every time forces it to re-render.
  const Button = React.memo(({ onClick }) => (
    <button onClick={onClick}>Click</button>
  ));

  function MyComponent() {
    // ❌ Bad: New function on every render → `<Button>` re-renders
    return <Button onClick={() => console.log("Clicked!")} />;
  }
  • Performance-sensitive components (e.g., large lists, frequent updates).

When Can You Ignore It?

  • Small apps or infrequent re-renders: The performance impact is negligible.
  • No memoized children: If child components don’t use React.memo, it’s less critical.

Key Takeaways

ApproachUse CasePerformance Impact
Inline arrow functionQuick prototyping❌ Bad (re-creates on every render)
Extract function outside JSXSimple handlers✅ Good (stable reference)
useCallbackHandlers with dependencies✅ Best (memoized)
Class component method bindingClass components✅ Good (bound once)

Final Recommendation

  • Default to extracting functions outside JSX.
  • Use useCallback when dependencies are involved.
  • Avoid inline arrow functions in performance-critical paths.

Leave a Reply

Your email address will not be published. Required fields are marked *