← Назад к вопросам

Как оптимизированы ререндеры в классовых компонентах в React?

1.7 Middle🔥 301 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Оптимизация ререндеров в классовых компонентах React

Хотя функциональные компоненты с хуками стали стандартом, классовые компоненты остаются важной частью большинства приложений. Понимание оптимизации ререндеров критично для производительности.

1. shouldComponentUpdate() - явное управление ререндерами

shouldComponentUpdate() - это метод жизненного цикла, который определяет, нужно ли перерендеривать компонент.

class UserProfile extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // Ререндер только если props или state изменились
    return (
      this.props.id !== nextProps.id ||
      this.state.isLoading !== nextState.isLoading
    )
  }

  render() {
    return <div>{this.props.id}</div>
  }
}

Осторожно: неправильная реализация может привести к багам, когда UI не обновляется несмотря на изменение данных.

2. React.PureComponent - поверхностное сравнение

PureComponent автоматически сравнивает props и state поверхностно (shallow comparison). Если они не изменились - ререндер не происходит.

class UserCard extends React.PureComponent {
  render() {
    return (
      <div>
        <h3>{this.props.user.name}</h3>
        <p>{this.props.user.email}</p>
      </div>
    )
  }
}

Как это работает:

// Если props не изменились - нет ререндера
const user1 = { name: 'John', email: 'john@example.com' }
const user2 = { name: 'John', email: 'john@example.com' }

user1 === user2 // false - разные объекты
// Поэтому PureComponent ПЕРЕРЕНДНЁРИТ, даже если значения одинаковы

Это - важная ловушка! Если создавать объекты на каждый рендер - PureComponent не поможет.

3. Правильное использование PureComponent

// ПЛОХО - создаёт новый объект на каждый рендер
class Parent extends React.Component {
  render() {
    const user = { name: 'John' } // новый объект каждый раз
    return <UserCard user={user} />
  }
}

// ХОРОШО - объект создаётся один раз
class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.user = { name: 'John' }
  }

  render() {
    return <UserCard user={this.user} />
  }
}

4. Мемоизация методов и props

Методы нужно мемоизировать, иначе они создаются заново на каждый рендер:

class Form extends React.PureComponent {
  constructor(props) {
    super(props)
    // Мемоизируем метод в конструкторе
    this.handleSubmit = this.handleSubmit.bind(this)
  }

  handleSubmit() {
    console.log('Форма отправлена')
  }

  render() {
    return <button onClick={this.handleSubmit}>Отправить</button>
  }
}

Или используем arrow функции:

class Form extends React.PureComponent {
  // Arrow функция - автоматически привязана
  handleSubmit = () => {
    console.log('Форма отправлена')
  }

  render() {
    return <button onClick={this.handleSubmit}>Отправить</button>
  }
}

5. Избегание создания объектов в render

ПЛОХО:

class ListItem extends React.PureComponent {
  render() {
    // Создаёт новый объект стилей на каждый рендер
    const style = { color: 'red', fontSize: '14px' }
    return <div style={style}>{this.props.text}</div>
  }
}

ХОРОШО:

const ITEM_STYLE = { color: 'red', fontSize: '14px' }

class ListItem extends React.PureComponent {
  render() {
    return <div style={ITEM_STYLE}>{this.props.text}</div>
  }
}

6. Сравнение списков и массивов

Поверхностное сравнение не работает для массивов:

// ПЛОХО - новый массив на каждый рендер
class List extends React.PureComponent {
  render() {
    const items = [1, 2, 3]
    return (
      <ul>
        {items.map(item => <li key={item}>{item}</li>)}
      </ul>
    )
  }
}

// ХОРОШО - кешируем в состоянии
class List extends React.PureComponent {
  constructor(props) {
    super(props)
    this.items = [1, 2, 3]
  }

  render() {
    return (
      <ul>
        {this.items.map(item => <li key={item}>{item}</li>)}
      </ul>
    )
  }
}

7. Использование shouldComponentUpdate для сложной логики

class DataTable extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // Ряд дорогих сравнений
    if (this.props.items.length !== nextProps.items.length) {
      return true
    }

    // Сравниваем только критичные поля
    if (this.props.sortBy !== nextProps.sortBy) {
      return true
    }

    if (this.state.selectedIds !== nextState.selectedIds) {
      return true
    }

    return false
  }

  render() {
    return (
      <table>
        <tbody>
          {this.props.items.map(item => (
            <tr key={item.id}>{item.name}</tr>
          ))}
        </tbody>
      </table>
    )
  }
}

8. Продвинутое сравнение объектов

class UserProfile extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // Глубокое сравнение для сложных объектов
    return !shallowEqual(this.props, nextProps) ||
           !shallowEqual(this.state, nextState)
  }

  render() {
    return <div>{this.props.user.name}</div>
  }
}

// Утилита для поверхностного сравнения
function shallowEqual(obj1, obj2) {
  const keys1 = Object.keys(obj1)
  const keys2 = Object.keys(obj2)

  if (keys1.length !== keys2.length) return false

  for (let key of keys1) {
    if (obj1[key] !== obj2[key]) return false
  }

  return true
}

9. React.memo для функциональных компонентов (альтернатива)

Хотя это не классовый компонент, это современная альтернатива PureComponent:

const UserCard = React.memo(function UserCard({ user, onSelect }) {
  return (
    <div onClick={() => onSelect(user.id)}>
      {user.name}
    </div>
  )
}, (prevProps, nextProps) => {
  // Вернуть true если props равны (не ререндерить)
  return prevProps.user.id === nextProps.user.id
})

10. Профилирование ререндеров

Реакт девтулы помогают найти лишние ререндеры:

class Button extends React.Component {
  componentDidMount() {
    console.log('Button mounted')
  }

  componentDidUpdate(prevProps, prevState) {
    console.log('Button updated')
    console.log('Props changed:', prevProps !== this.props)
    console.log('State changed:', prevState !== this.state)
  }

  render() {
    console.log('Button rendering')
    return <button>{this.props.label}</button>
  }
}

Лучшие практики

  • Используйте PureComponent для простых компонентов без сложной логики
  • Мемоизируйте методы в конструкторе или используйте arrow функции
  • Избегайте создания объектов/массивов в методе render
  • Используйте shouldComponentUpdate для специальных случаев
  • Профилируйте перед оптимизацией - не оптимизируйте вслепую
  • Помните о shallow comparison - она работает только для простых типов
  • Для нового кода рассмотрите функциональные компоненты с useMemo/useCallback

Оптимизация ререндеров - это баланс между производительностью и сложностью кода. Иногда лишний ререндер дешевле, чем код для его предотвращения.

Как оптимизированы ререндеры в классовых компонентах в React? | PrepBro