React Js

React JS Tutorial Learn React JS, the powerful JavaScript library for building dynamic and interactive user interfaces. This beginner-friendly tutorial covers core concepts like components, state, props, and hooks, with hands-on projects to create responsive web apps. Perfect for aspiring developers looking to master modern front-end development.

React Memo: A Beginner’s Guide

24 April 2025 | Category:

Welcome to this beginner-friendly tutorial on React Memo! React.memo is a higher-order component in React that optimizes performance by preventing unnecessary re-renders of components when their props haven’t changed. In this guide, you’ll learn what React.memo is, how it works, when to use it, and how to combine it with other React features. We’ll apply these concepts by building a Todo List App with a list of todos and a filter, demonstrating how React.memo improves performance. By the end, you’ll be confident using React.memo to optimize your React applications for better performance.


What is React Memo?

React.memo is a higher-order component (HOC) that memoizes a functional component, ensuring it only re-renders when its props change. By default, React re-renders a component whenever its parent re-renders or its state/props change, even if the props are unchanged. React.memo performs a shallow comparison of props to skip re-renders, improving performance for components with expensive rendering logic or frequent parent updates.

Why Learn React Memo?

  • Performance Optimization: Reduce unnecessary re-renders to make apps faster, especially for large lists or complex components.
  • Scalability: Ensure apps remain performant as they grow with more components and state updates.
  • User Experience: Prevent UI lag in apps with frequent updates, like filters or real-time data.
  • Core Skill: Understanding memoization is key for building efficient React applications.

Prerequisites

Before starting, you should have:

  • Basic knowledge of React (components, props, state, JSX, events, conditional rendering, lists, forms) and JavaScript (ES6).
  • Node.js and npm installed (download from nodejs.org).
  • A code editor like Visual Studio Code.
  • A terminal for running commands.

We’ll use Create React App to set up our project and Tailwind CSS for responsive, attractive styling, consistent with your preference for visually appealing designs. We’ll also use React.memo from React’s core library (no additional installation needed).


Key React Memo Concepts

Let’s explore the core concepts for using React.memo.

1. Basic Usage of React.memo

Wrap a functional component with React.memo to memoize it. The component only re-renders if its props change (via shallow comparison).

Example:

import React from 'react';

const Item = ({ name }) => {
  console.log('Item rendered');
  return <div>{name}</div>;
};

export default React.memo(Item);

2. When to Use React.memo

Use React.memo for:

  • Components that render frequently due to parent re-renders.
  • Components with expensive rendering logic (e.g., complex calculations or large lists).
  • Components with stable props that rarely change.

Example:

const ExpensiveComponent = ({ data }) => {
  // Complex rendering logic
  return <div>{data}</div>;
};

export default React.memo(ExpensiveComponent);

3. Shallow Prop Comparison

React.memo compares props shallowly, meaning it checks if primitive props (e.g., strings, numbers) are identical and if object/array props have the same reference. If props are objects, ensure they don’t change unnecessarily.

Example:

// Bad: Creates a new object on every render
const Parent = () => {
  const obj = { value: 1 };
  return <Child data={obj} />;
};

// Good: Stable object reference
const obj = { value: 1 };
const Parent = () => {
  return <Child data={obj} />;
};

const Child = React.memo(({ data }) => <div>{data.value}</div>);

4. Combining with useCallback and useMemo

When passing functions or computed values as props, use useCallback or useMemo to prevent new references on each render, ensuring React.memo works effectively.

Example:

const Parent = () => {
  const [count, setCount] = React.useState(0);
  const handleClick = React.useCallback(() => {
    console.log('Clicked');
  }, []);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Count: {count}</button>
      <Child onClick={handleClick} />
    </div>
  );
};

const Child = React.memo(({ onClick }) => {
  console.log('Child rendered');
  return <button onClick={onClick}>Click Me</button>;
});

5. When Not to Use React.memo

Avoid React.memo for:

  • Components that re-render frequently due to changing props or state.
  • Simple components with minimal rendering cost (e.g., a single <div>).
  • Over-optimization, as it adds memory overhead and complexity.

Example:

// Unnecessary: Simple component with frequent updates
const Simple = ({ value }) => <span>{value}</span>;
export default React.memo(Simple); // Likely overkill

Setting Up the Project

Let’s create a React app to build our Todo List App, which will use React.memo to optimize rendering of todo items.

  1. Create a New React App:
    Open your terminal and run: npx create-react-app todo-list cd todo-list
  2. Install Tailwind CSS:
    Install and configure Tailwind CSS for responsive styling: npm install -D tailwindcss npx tailwindcss init Update tailwind.config.js: /** @type {import('tailwindcss').Config} */ module.exports = { content: ['./src/**/*.{js,jsx,ts,tsx}'], theme: { extend: {} }, plugins: [] }; Update src/index.css: @tailwind base; @tailwind components; @tailwind utilities; body { font-family: Arial, sans-serif; margin: 0; background-color: #f5f5f5; }
  3. Start the Development Server: npm start This opens your app at http://localhost:3000.
  4. Clean Up:
    Open src/App.js and replace its content with: const App = () => { return ( <div className="container mx-auto p-4"> <h1 className="text-3xl font-bold text-gray-800">Todo List</h1> </div> ); }; export default App; Delete src/App.css and src/logo.svg.

Building the Todo List App

Our Todo List App will:

  • Display a list of todos using a TodoItem component, optimized with React.memo to prevent unnecessary re-renders.
  • Allow users to add new todos via a TodoForm component.
  • Include a filter to show all, completed, or pending todos, triggering frequent parent re-renders to demonstrate React.memo’s benefits.
  • Use Tailwind CSS for a responsive, visually appealing design, aligning with your preference for attractive UI.

Step 1: Create the TodoItem Component

  1. In src, create a file named TodoItem.js:import React from 'react'; const TodoItem = ({ id, title, isCompleted, onToggle, onDelete }) => { console.log(`TodoItem ${id} rendered`); return ( <div className="flex items-center bg-white p-4 rounded-lg shadow-md mb-4 max-w-md"> <div className="flex-1"> <h3 className={`text-lg font-semibold ${ isCompleted ? 'text-gray-400 line-through' : 'text-gray-800' }`} > {title} </h3> <p className={isCompleted ? 'text-green-500' : 'text-yellow-500'}> {isCompleted ? 'Completed' : 'Pending'} </p> </div> <div className="flex space-x-2"> <button onClick={() => onToggle(id)} className={`px-3 py-1 rounded-md ${ isCompleted ? 'bg-yellow-500 hover:bg-yellow-600' : 'bg-green-500 hover:bg-green-600' } text-white`} > {isCompleted ? 'Undo' : 'Complete'} </button> <button onClick={() => onDelete(id)} className="px-3 py-1 bg-red-500 text-white rounded-md hover:bg-red-600" > Delete </button> </div> </div> ); }; export default React.memo(TodoItem);
    • Memo Features:
      • Wrapped with React.memo to prevent re-renders when props (id, title, isCompleted, onToggle, onDelete) don’t change.
      • Logs renders to demonstrate memoization (check console during filter changes).
      • Uses conditional rendering for completed/pending styles and button text.
      • Styled with Tailwind CSS for a responsive, card-based layout.

Step 2: Create the TodoForm Component

  1. In src, create a file named TodoForm.js:const TodoForm = ({ onAddTodo }) => { const [title, setTitle] = React.useState(''); const handleSubmit = (e) => { e.preventDefault(); if (title.trim()) { onAddTodo({ title, isCompleted: false }); setTitle(''); } }; return ( <form onSubmit={handleSubmit} className="mb-6 max-w-md"> <div className="flex space-x-2"> <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Add a todo" className="flex-1 p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" /> <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600" > Add </button> </div> </form> ); }; export default TodoForm;
    • Memo Features:
      • A controlled form for adding todos, passing data via onAddTodo.
      • Not memoized, as it manages its own state and re-renders appropriately.
      • Styled with Tailwind CSS for a compact, responsive form.

Step 3: Update the App Component

  1. Update src/App.js to manage todos, filters, and render the app:import TodoForm from './TodoForm'; import TodoItem from './TodoItem'; const App = () => { const [todos, setTodos] = React.useState([ { id: '1', title: 'Learn React Memo', isCompleted: true }, { id: '2', title: 'Build Todo App', isCompleted: false } ]); const [filter, setFilter] = React.useState('all'); const addTodo = (newTodo) => { setTodos([...todos, { ...newTodo, id: Date.now().toString() }]); }; const toggleTodo = React.useCallback((id) => { setTodos((prev) => prev.map((todo) => todo.id === id ? { ...todo, isCompleted: !todo.isCompleted } : todo ) ); }, []); const deleteTodo = React.useCallback((id) => { setTodos((prev) => prev.filter((todo) => todo.id !== id)); }, []); const filteredTodos = todos.filter((todo) => { if (filter === 'completed') return todo.isCompleted; if (filter === 'pending') return !todo.isCompleted; return true; }); return ( <div className="container mx-auto p-4"> <h1 className="text-3xl font-bold text-gray-800 mb-6">Todo List</h1> <TodoForm onAddTodo={addTodo} /> <div className="flex space-x-2 mb-4"> <button onClick={() => setFilter('all')} className={`px-4 py-2 rounded-md ${ filter === 'all' ? 'bg-blue-500 text-white' : 'bg-gray-200' }`} > All </button> <button onClick={() => setFilter('completed')} className={`px-4 py-2 rounded-md ${ filter === 'completed' ? 'bg-blue-500 text-white' : 'bg-gray-200' }`} > Completed </button> <button onClick={() => setFilter('pending')} className={`px-4 py-2 rounded-md ${ filter === 'pending' ? 'bg-blue-500 text-white' : 'bg-gray-200' }`} > Pending </button> </div> <div className="grid grid-cols-1 sm:grid-cols-2 gap-4"> {filteredTodos.length > 0 ? ( filteredTodos.map((todo) => ( <TodoItem key={todo.id} id={todo.id} title={todo.title} isCompleted={todo.isCompleted} onToggle={toggleTodo} onDelete={deleteTodo} /> )) ) : ( <p className="text-gray-500">No todos match the selected filter.</p> )} </div> </div> ); }; export default App;
    • Memo Features:
      • Uses React.memo on TodoItem to prevent re-renders when filter changes but todo props remain the same.
      • Wraps toggleTodo and deleteTodo in useCallback to ensure stable function references, preventing unnecessary TodoItem re-renders.
      • Demonstrates frequent parent re-renders (via filter changes) to show React.memo’s benefits (check console logs).
      • Uses conditional rendering for empty states and list rendering with unique key props.
      • Styled with Tailwind CSS for a responsive grid layout.

Step 4: Test the App

  • Save all files and ensure the development server is running (npm start).
  • Open http://localhost:3000 and open the browser console (F12 → Console). You should see:
    • Two initial todos (“Learn React Memo” and “Build Todo App”) in a responsive grid.
    • A form to add new todos.
    • Filter buttons to show all, completed, or pending todos.
    • Console logs for TodoItem renders (e.g., “TodoItem 1 rendered”).
  • Try:
    • Adding a new todo (e.g., “Test Memo”).
    • Toggling a todo’s completion status or deleting it.
    • Clicking filter buttons (All, Completed, Pending) and checking the console. Without React.memo, every TodoItem would re-render on filter changes; with React.memo, only items with changed props (or new items) render.
    • Verifying responsiveness by resizing the browser or using mobile view in dev tools.

Understanding the Code

Let’s recap how React.memo powers our Todo List App:

  • Memoization: TodoItem is wrapped with React.memo, preventing re-renders when props (id, title, isCompleted, onToggle, onDelete) don’t change, even when the parent (App) re-renders due to filter updates.
  • Stable Props: useCallback ensures toggleTodo and deleteTodo maintain the same reference, allowing React.memo to skip re-renders effectively.
  • Performance Demonstration: Filter changes cause App to re-render, but TodoItem components only log renders when their props change (e.g., on toggle or delete), showing React.memo’s optimization.
  • Responsive Design: Tailwind CSS ensures a mobile-friendly grid layout with clean buttons and cards, aligning with your preference for attractive, responsive UI.
  • ES6 Features:
    • Arrow functions for components and handlers.
    • Destructuring props ({ id, title, isCompleted, ... }).
    • Spread operator for state updates ([...todos, newTodo]).
    • Modules for component organization.

Best Practices for React Memo

  • Use Sparingly: Apply React.memo only to components with expensive renders or frequent parent re-renders:const ComplexComponent = React.memo(({ data }) => { ... });
  • Ensure Stable Props: Use useCallback for function props and useMemo for object/array props to prevent unnecessary reference changes:const handleClick = React.useCallback(() => {}, []); const data = React.useMemo(() => ({ value: 1 }), []);
  • Test Performance: Use browser dev tools (e.g., React Profiler) to confirm React.memo improves performance before adding it.
  • Combine with Other Optimizations: Pair with useMemo for computed values or useCallback for handlers:const computed = React.useMemo(() => expensiveCalculation(data), [data]);
  • Avoid Overuse: Don’t memoize simple components or those with frequently changing props, as it adds overhead:// Unnecessary const Simple = React.memo(({ value }) => <span>{value}</span>);
  • Debug with Logs: Add console.log to components to verify re-renders and confirm React.memo’s effectiveness.

Common React Memo Pitfalls and Fixes

  • Unstable Props:
    Problem: React.memo doesn’t prevent re-renders if props (e.g., functions, objects) change references unnecessarily.
    Fix: Use useCallback or useMemo:const handleClick = React.useCallback(() => {}, []);
  • Over-Memoization:
    Problem: Memoizing every component adds complexity and memory overhead.
    Fix: Only memoize components with measurable performance issues.
  • Deep Prop Changes:
    Problem: React.memo only does shallow comparison, so deep object changes may not trigger re-renders.
    Fix: Use a custom comparison function with React.memo:const MyComponent = React.memo( ({ data }) => <div>{data.value}</div>, (prevProps, nextProps) => prevProps.data.value === nextProps.data.value );
  • Ignoring State Changes:
    Problem: React.memo doesn’t prevent re-renders due to internal state changes.
    Fix: Move state to a parent or child component if memoization is critical.

React Memo in Functional vs. Class Components

Since you’ve explored class components previously, here’s how memoization differs:

  • Functional Components: Use React.memo to wrap the entire component:const Item = React.memo(({ name }) => <div>{name}</div>);
  • Class Components: React.memo is for functional components only. For class components, use PureComponent or shouldComponentUpdate:class Item extends React.PureComponent { render() { return <div>{this.props.name}</div>; } }

Our app uses functional components with React.memo for simplicity and alignment with modern React, but PureComponent achieves similar optimization for classes.


What’s Next?

You’ve built a Todo List App using React.memo! Here are some next steps:

  • Add Features: Implement an “Edit Todo” feature, ensuring React.memo doesn’t interfere with state updates.
  • Learn More Optimizations: Explore useMemo for memoizing values or React’s Profiler for performance analysis.
  • Enhance Styling: Add animations with Tailwind CSS or Framer Motion for todo transitions.
  • Build Another App: Create a dashboard or product list with memoized components to practice optimization.

Practice Challenge

Add a “Priority” field to todos (e.g., Low, Medium, High) and memoize a PriorityBadge component that displays the priority with a color (e.g., red for High). Use useCallback for any passed handlers and verify that PriorityBadge only re-renders when priority changes.


Resources

Congratulations on mastering React.memo! You’re now equipped to optimize your React applications for better performance. Keep practicing and happy coding!