componentDidMount и componentDidUpdate

В Preact, как и в React, жизненный цикл компонентов классов предоставляет набор методов, позволяющих управлять поведением компонентов на различных этапах их существования. Среди них ключевое значение имеют componentDidMount и componentDidUpdate, которые отвечают за действия после монтирования компонента в DOM и после его обновления соответственно.


componentDidMount

Метод componentDidMount вызывается один раз после того, как компонент был вставлен в DOM. Это подходящее место для:

  • Запуска сторонних библиотек, требующих DOM-узлы.
  • Инициализации подписок на события.
  • Получения данных с сервера через API-запросы.

Пример использования:

import { h, Component } from 'preact';

class UserList extends Component {
    state = {
        users: []
    };

    componentDidMount() {
        fetch('https://jsonplaceholder.typicode.com/users')
            .then(response => response.json())
            .then(data => this.setState({ users: data }));
    }

    render() {
        return (
            <ul>
                {this.state.users.map(user => (
                    <li key={user.id}>{user.name}</li>
                ))}
            </ul>
        );
    }
}

Ключевой момент: componentDidMount вызывается только один раз. Любые обновления состояния после этого метода приведут к повторному рендеру, но сам метод не вызовется повторно.


componentDidUpdate

Метод componentDidUpdate вызывается после каждого обновления компонента, когда изменились props или state. Он позволяет выполнять операции, зависящие от новых данных, или синхронизировать состояние с внешними источниками.

Сигнатура метода:

componentDidUpdate(prevProps, prevState, snapshot)
  • prevProps — предыдущие свойства компонента.
  • prevState — предыдущее состояние компонента.
  • snapshot — необязательный параметр, возвращаемый методом getSnapshotBeforeUpdate, используется для сохранения информации о DOM перед обновлением.

Пример использования:

class Timer extends Component {
    state = { seconds: 0 };

    componentDidUpdate(prevProps, prevState) {
        if (prevState.seconds !== this.state.seconds) {
            console.log(`Прошло ${this.state.seconds} секунд`);
        }
    }

    tick = () => {
        this.setState(state => ({ seconds: state.seconds + 1 }));
    };

    componentDidMount() {
        this.interval = setInterval(this.tick, 1000);
    }

    componentWillUnmount() {
        clearInterval(this.interval);
    }

    render() {
        return <div>Секунды: {this.state.seconds}</div>;
    }
}

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


Отличия и взаимодействие

Метод Время вызова Основное назначение
componentDidMount После первого рендера компонента Инициализация данных, работа с DOM, запуск таймеров
componentDidUpdate После каждого обновления Реакция на изменения props или state, синхронизация данных

Механизм работы жизненного цикла:

  1. Рендер компонента — вызывается render().
  2. Вставка в DOM — вызывается componentDidMount.
  3. Обновление props или state — вызывается render().
  4. Завершение обновления — вызывается componentDidUpdate.

Практические рекомендации

  • Любые побочные эффекты, требующие доступа к DOM или внешним API, следует помещать в componentDidMount.
  • В componentDidUpdate следует аккуратно использовать setState, всегда проверяя, изменилось ли состояние или props.
  • Для асинхронных операций (fetch, setTimeout, setInterval) необходимо очищать ресурсы в componentWillUnmount, чтобы избежать утечек памяти.
  • Можно использовать getSnapshotBeforeUpdate для получения информации о DOM перед обновлением, которая затем передается в componentDidUpdate.

Пример синхронизации данных при обновлении props

class Profile extends Component {
    state = { profileData: null };

    componentDidMount() {
        this.fetchProfile(this.props.userId);
    }

    componentDidUpdate(prevProps) {
        if (prevProps.userId !== this.props.userId) {
            this.fetchProfile(this.props.userId);
        }
    }

    fetchProfile(userId) {
        fetch(`https://api.example.com/users/${userId}`)
            .then(response => response.json())
            .then(data => this.setState({ profileData: data }));
    }

    render() {
        if (!this.state.profileData) return <div>Загрузка...</div>;
        return (
            <div>
                <h1>{this.state.profileData.name}</h1>
                <p>{this.state.profileData.email}</p>
            </div>
        );
    }
}

В этом примере componentDidUpdate обеспечивает повторную загрузку данных только при изменении идентификатора пользователя, предотвращая лишние запросы и ререндеры.


Хотя Preact имеет более лёгкий и быстрый механизм по сравнению с React, жизненный цикл компонентов и методы componentDidMount и componentDidUpdate остаются практически идентичными, что позволяет использовать схожие подходы к управлению состоянием и асинхронными операциями.