Skip to main content

PureComponent

A component class with a predefined shouldComponentUpdate implementation that performs shallow comparison of props and state.

Signature

abstract class PureComponent<P = {}, S = {}> extends Component<P, S> {
  isPureReactComponent: boolean;
  shouldComponentUpdate(nextProps: Readonly<P>, nextState: Readonly<S>): boolean;
}

Usage

Basic Example

Extend PureComponent instead of Component:
import { PureComponent } from 'preact/compat';

class UserCard extends PureComponent {
  render() {
    const { name, email, avatar } = this.props;
    
    return (
      <div className="user-card">
        <img src={avatar} alt={name} />
        <h3>{name}</h3>
        <p>{email}</p>
      </div>
    );
  }
}

// This component will only re-render when props change

With State

PureComponent also compares state changes:
import { PureComponent } from 'preact/compat';

class Counter extends PureComponent {
  state = {
    count: 0,
    label: 'Counter'
  };
  
  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };
  
  render() {
    return (
      <div>
        <h3>{this.state.label}</h3>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>+1</button>
      </div>
    );
  }
}

TypeScript

Type your props and state:
import { PureComponent } from 'preact/compat';

interface TodoProps {
  id: number;
  text: string;
  completed: boolean;
  onToggle: (id: number) => void;
}

class TodoItem extends PureComponent<TodoProps> {
  handleClick = () => {
    this.props.onToggle(this.props.id);
  };
  
  render() {
    const { text, completed } = this.props;
    
    return (
      <li
        onClick={this.handleClick}
        style={{ textDecoration: completed ? 'line-through' : 'none' }}
      >
        {text}
      </li>
    );
  }
}

Implementation Details

The PureComponent implementation in Preact:
export function PureComponent(p, c) {
  this.props = p;
  this.context = c;
}

PureComponent.prototype = new Component();
PureComponent.prototype.isPureReactComponent = true;
PureComponent.prototype.shouldComponentUpdate = function (props, state) {
  return shallowDiffers(this.props, props) || shallowDiffers(this.state, state);
};

Shallow Comparison

The shallowDiffers function checks if objects differ:
function shallowDiffers(a, b) {
  for (let i in a) if (i !== '__source' && !(i in b)) return true;
  for (let i in b) if (i !== '__source' && a[i] !== b[i]) return true;
  return false;
}
This returns true if:
  • Any keys exist in one object but not the other
  • Any values differ using strict equality (!==)

When to Use PureComponent

Good Use Cases ✅

  1. Components with simple props: Props that are primitives or stable references
  2. Frequently updated lists: List items that receive the same props often
  3. Performance optimization: Components that re-render unnecessarily
class ListItem extends PureComponent {
  render() {
    return <li>{this.props.text}</li>;
  }
}

Problematic Cases ❌

  1. Props with new objects: Creating new objects on every render defeats the purpose
// Bad: Creates new object every render
<UserCard user={{ name: 'John', age: 30 }} />

// Good: Use stable reference
const user = { name: 'John', age: 30 };
<UserCard user={user} />
  1. Props with new arrays: New array references cause unnecessary re-renders
// Bad: Creates new array every render
<List items={this.state.data.map(x => x.name)} />

// Good: Memoize the array
const names = useMemo(
  () => this.state.data.map(x => x.name),
  [this.state.data]
);
<List items={names} />
  1. Props with inline functions: New function references on every render
// Bad: Creates new function every render
<Button onClick={() => this.handleClick(item.id)} />

// Good: Use class method or memoized callback
<Button onClick={this.handleClick} />

PureComponent vs Component

FeatureComponentPureComponent
shouldComponentUpdateNot implementedShallow comparison
Re-rendersOn any setState() or prop changeOnly when props/state differ
PerformanceBaselineFaster for stable props
Use caseMost componentsFrequently updated components

PureComponent vs memo

  • PureComponent: For class components
  • memo: For functional components
// Class component
class UserCard extends PureComponent {
  render() {
    return <div>{this.props.name}</div>;
  }
}

// Functional component
const UserCard = memo(({ name }) => (
  <div>{name}</div>
));
Both provide similar optimization but for different component types.

Overriding shouldComponentUpdate

You can override shouldComponentUpdate in a PureComponent for custom logic:
class CustomPure extends PureComponent {
  shouldComponentUpdate(nextProps, nextState) {
    // Custom comparison logic
    return this.props.userId !== nextProps.userId;
  }
  
  render() {
    return <div>{this.props.userName}</div>;
  }
}
Note: This defeats the purpose of PureComponent. Use regular Component instead.

Common Patterns

List Items

import { PureComponent } from 'preact/compat';

class TodoItem extends PureComponent {
  render() {
    const { todo, onToggle, onDelete } = this.props;
    
    return (
      <li>
        <input
          type="checkbox"
          checked={todo.completed}
          onChange={() => onToggle(todo.id)}
        />
        <span>{todo.text}</span>
        <button onClick={() => onDelete(todo.id)}>Delete</button>
      </li>
    );
  }
}

class TodoList extends Component {
  // Bind methods in constructor to maintain stable references
  constructor(props) {
    super(props);
    this.handleToggle = this.handleToggle.bind(this);
    this.handleDelete = this.handleDelete.bind(this);
  }
  
  handleToggle(id) {
    // Toggle logic
  }
  
  handleDelete(id) {
    // Delete logic
  }
  
  render() {
    return (
      <ul>
        {this.state.todos.map(todo => (
          <TodoItem
            key={todo.id}
            todo={todo}
            onToggle={this.handleToggle}
            onDelete={this.handleDelete}
          />
        ))}
      </ul>
    );
  }
}

With Context

import { PureComponent, createContext } from 'preact/compat';

const ThemeContext = createContext('light');

class ThemedButton extends PureComponent {
  static contextType = ThemeContext;
  
  render() {
    return (
      <button className={`btn-${this.context}`}>
        {this.props.children}
      </button>
    );
  }
}

Identifying Pure Components

Check if a component is a PureComponent:
class MyComponent extends PureComponent {}

console.log(MyComponent.prototype.isPureReactComponent); // true
console.log(new MyComponent().isPureReactComponent); // true

Best Practices

  1. Stable Props: Ensure props are stable references or primitives
  2. Avoid Inline Objects: Don’t create new objects in render
  3. Avoid Inline Functions: Use class methods or memoized callbacks
  4. Measure Performance: Profile before and after to ensure it helps
  5. Consider Hooks: For new code, consider using functional components with memo instead

Migration to Functional Components

Modern Preact/React development favors functional components with hooks:
// Before: Class with PureComponent
class UserCard extends PureComponent {
  render() {
    return <div>{this.props.name}</div>;
  }
}

// After: Functional component with memo
import { memo } from 'preact/compat';

const UserCard = memo(({ name }) => (
  <div>{name}</div>
));

Source

Implementation: compat/src/PureComponent.js:1-17

Build docs developers (and LLMs) love