![]()
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.memoorshouldComponentUpdate). - 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:
useCallbackmemoizes the function and only recreates it whenuserIdchanges.- 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
useCallbackin 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
| Approach | Use Case | Performance Impact |
|---|---|---|
| Inline arrow function | Quick prototyping | ❌ Bad (re-creates on every render) |
| Extract function outside JSX | Simple handlers | ✅ Good (stable reference) |
useCallback | Handlers with dependencies | ✅ Best (memoized) |
| Class component method binding | Class components | ✅ Good (bound once) |
Final Recommendation
- Default to extracting functions outside JSX.
- Use
useCallbackwhen dependencies are involved. - Avoid inline arrow functions in performance-critical paths.
