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

В чем разница между рендером и ререндером у функциональных и классовых компонентов в React?

2.0 Middle🔥 191 комментариев
#React

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

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

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

Разница между рендером и ререндером в функциональных и классовых компонентах React

Определения

Рендер — первое отображение компонента на странице. Ререндер — повторное отображение компонента после изменения состояния, props или контекста.

Оба типа компонентов работают по одному принципу, но имеют существенные различия в реализации.

Классовые компоненты

Рендер (начальный)

  1. Вызывается конструктор (constructor)
  2. Инициализируется состояние
  3. Монтируются хуки жизненного цикла
  4. Вызывается render() — возвращает JSX
  5. React создает DOM элементы
  6. Вызывается componentDidMount() — после монтирования
class Counter extends React.Component {
  constructor(props) {
    super(props);
    console.log("1. Constructor");
    this.state = { count: 0 };
  }
  
  componentDidMount() {
    console.log("4. componentDidMount - компонент смонтирован");
  }
  
  render() {
    console.log("3. Render");
    return <div>Count: {this.state.count}</div>;
  }
}

// Вывод:
// 1. Constructor
// 3. Render
// 4. componentDidMount

Ререндер (при изменении состояния)

  1. Изменяется состояние через setState()
  2. Вызывается shouldComponentUpdate() (опционально)
  3. Вызывается render() снова
  4. React обновляет DOM
  5. Вызывается componentDidUpdate() — после обновления
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  
  shouldComponentUpdate(nextProps, nextState) {
    console.log("Должно ли обновляться?");
    return true; // да, обновляй
  }
  
  componentDidUpdate(prevProps, prevState) {
    console.log("Компонент обновлен!");
    if (prevState.count !== this.state.count) {
      console.log(`Count изменился с ${prevState.count} на ${this.state.count}`);
    }
  }
  
  render() {
    console.log("Render в процессе обновления");
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          +
        </button>
      </div>
    );
  }
}

// Нажимаем кнопку:
// Должно ли обновляться?
// Render в процессе обновления
// Компонент обновлен!
// Count изменился с 0 на 1

Проблемы классовых компонентов

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  
  // Нужно биндить методы
  handleIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  };
  
  render() {
    return (
      <div>
        <Counter count={this.state.count} />
        <button onClick={this.handleIncrement}>Increment</button>
      </div>
    );
  }
}

Функциональные компоненты

Рендер (начальный)

  1. React вызывает функцию компонента
  2. Выполняются хуки (useState, useEffect, и т.д.)
  3. Первый render выполняется
  4. Возвращается JSX
  5. React создает DOM элементы
  6. Эффекты (useEffect) с пустым массивом зависимостей выполняются после монтирования
function Counter() {
  console.log("1. Функция вызвана (до хуков)");
  
  const [count, setCount] = useState(0);
  console.log("2. useState вызван, count =", count);
  
  useEffect(() => {
    console.log("4. useEffect с [] - компонент смонтирован");
    return () => console.log("Cleanup");
  }, []);
  
  console.log("3. Функция возвращает JSX");
  return <div>Count: {count}</div>;
}

// Вывод при монтировании:
// 1. Функция вызвана (до хуков)
// 2. useState вызван, count = 0
// 3. Функция возвращает JSX
// 4. useEffect с [] - компонент смонтирован

Ререндер (при изменении состояния)

  1. React вызывает функцию компонента снова
  2. Хуки выполняются в том же порядке
  3. useState возвращает новое значение
  4. Возвращается новый JSX
  5. React обновляет DOM
  6. useEffect с зависимостями проверяет изменения
function Counter() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    console.log("Effect выполнен, count =", count);
    return () => console.log("Cleanup перед следующим effect");
  }, [count]); // Зависит от count
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}

// Нажимаем кнопку (count: 0 -> 1):
// Cleanup перед следующим effect
// Effect выполнен, count = 1

Ключевые различия

1. Код выполняется

Классовые компоненты:

  • Код вне методов не выполняется снова при ререндере
  • Только методы жизненного цикла срабатывают заново
class Counter extends React.Component {
  x = 0; // Инициализируется один раз
  
  render() {
    console.log("x =", this.x); // Всегда 0
    return <div>x = {this.x}</div>;
  }
}

Функциональные компоненты:

  • Вся функция выполняется заново при ререндере
  • Это может быть неэффективно, но дает больше контроля
function Counter() {
  let x = 0; // Создается заново при каждом ререндере!
  
  return <div>x = {x}</div>;
}

2. Замыкания (Closures)

Функциональные компоненты: Каждый ререндер создает новый замыкание с новыми переменными.

function Counter() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    const timer = setTimeout(() => {
      console.log("Count в этом эффекте:", count);
    }, 3000);
    
    return () => clearTimeout(timer);
  }, [count]); // Зависит от count
  
  return <div>{count}</div>;
}

// Если count меняется быстро, вывод будет правильным

3. Оптимизация

Классовые компоненты:

class Parent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // Ручная проверка, нужно ли обновляться
    return nextProps.count !== this.props.count;
  }
  
  render() {
    return <Child count={this.props.count} />;
  }
}

Функциональные компоненты:

const Parent = React.memo(({ count }) => {
  return <Child count={count} />;
});
// React.memo автоматически сравнивает props

// Или внутри компонента
function Component() {
  const memoValue = useMemo(() => expensiveCalculation(), [deps]);
  const memoCallback = useCallback(() => {}, [deps]);
}

Сравнительная таблица

АспектКлассовые компонентыФункциональные компоненты
Рендерrender() методВся функция вызывается
РерендерТолько в методах жизненного циклаВся функция вызывается заново
Состояниеthis.stateuseState хук
Обновление состоянияthis.setState()Функция из useState
Побочные эффектыcomponentDidMount, componentDidUpdateuseEffect
ОптимизацияshouldComponentUpdate, PureComponentReact.memo, useMemo, useCallback
Контроль выполненияЖесткий контрольГибкий, но требует внимания к зависимостям
ПроизводительностьХорошая (если оптимизировано)Может быть лучше (меньше кода)

Практический пример

Классовый компонент:

class DataFetcher extends React.Component {
  constructor(props) {
    super(props);
    this.state = { data: null, loading: true };
  }
  
  componentDidMount() {
    fetch(this.props.url)
      .then(res => res.json())
      .then(data => this.setState({ data, loading: false }));
  }
  
  componentDidUpdate(prevProps) {
    if (prevProps.url !== this.props.url) {
      this.setState({ loading: true });
      fetch(this.props.url)
        .then(res => res.json())
        .then(data => this.setState({ data, loading: false }));
    }
  }
  
  render() {
    if (this.state.loading) return <p>Loading...</p>;
    return <div>{JSON.stringify(this.state.data)}</div>;
  }
}

Функциональный компонент (эквивалент):

function DataFetcher({ url }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    setLoading(true);
    fetch(url)
      .then(res => res.json())
      .then(data => {
        setData(data);
        setLoading(false);
      });
  }, [url]);
  
  if (loading) return <p>Loading...</p>;
  return <div>{JSON.stringify(data)}</div>;
}

Итог

Рендер в обоих типах компонентов — это возвращение JSX для создания DOM. Ререндер в классовых компонентах контролируется жестко через методы жизненного цикла, а в функциональных компонентах вся функция вызывается заново. Функциональные компоненты с хуками — современный стандарт и проще в использовании, но требуют понимания замыканий и зависимостей. Классовые компоненты дают больше контроля, но сложнее в коде.