Темы React: жизненный цикл

внешний интерфейс JavaScript React.js

Эта статья является одной из серии статей на тему «подкова · React», и в будущем будет запущено больше тем.

иди ко мнеGitHub repoПрочитайте полную тему статья

иди ко мнеличный блогПолучите непревзойденный опыт чтения

Жизненный цикл, как следует из названия, представляет собой процесс от рождения до смерти.

Крюк жизненного цикла — ключевой узел в процессе от рождения до смерти.

Какие зацепки жизненного цикла есть в жизни обычного человека?

  • Родился
  • принят в университет
  • Первая работа
  • купить дом
  • выходит замуж
  • рожать
  • Крючки дочернего жизненного цикла
  • выходить на пенсию
  • Последние слова

В каждый критический момент нам всегда хочется немного поразмышлять, потому что решения, которые мы принимаем в это время, изменят направление нашей жизни.

То же самое касается компонентов React, которые дадут разработчикам несколько моментов для размышлений, когда они могут изменить направление компонента.

Жизненный цикл при асинхронном рендеринге

React потребовалось два года, чтобы придумать механизм рендеринга Fiber.

Проще говоря, React называет процесс сравнения согласованием. Раньше этот процесс выполнялся за один раз, но механизм Fiber изменил его на асинхронный. Асинхронные навыки будут постепенно разблокированы в следующей версии.

Очевидно, что это синхронный код, почему он асинхронный?

Принцип заключается в том, что Файбер делит задачу на мелкие части, и возвращает управление основному потоку каждый раз, когда часть выполняется, а затем выполняет оставшиеся задачи после того, как основной поток занят текущей работой. Конечно, если время выполнения какого-то куска очень велико (например, бесконечный цикл), то основной поток тут ни при чем, и пора крашиться.

Как это влияет на жизненный цикл?

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

Почему ты это сказал? Поскольку процесс согласования может быть приостановлен, а затем продолжать выполняться, обработчик жизненного цикла перед монтированием и обновлением может не выполняться или выполняться несколько раз, а его производительность непредсказуема.

Таким образом, жизненный цикл React после 16 лет принес волну изменений, и постепенно будут отказываться от следующих зацепок жизненного цикла:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

Вы видите характеристики, они все имеютwillкрюк.

React в настоящее время предоставляет псевдонимы для этих хуков жизненного цикла, а именно:

  • UNSAFE_componentWillMount
  • UNSAFE_componentWillReceiveProps
  • UNSAFE_componentWillUpdate

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

constructor()

React заимствует классconstructorДействует как хук инициализации.

React почти ничего не делает, но поскольку мы разрешаем передавать параметры компонентам только по определенным путям, поэтомуconstructorПараметры фактически определяются React.

Реагировать оговариваетconstructorЕсть три параметра, которыеprops,contextа такжеupdater.

  • propsявляется свойством, оно неизменно.
  • contextявляется глобальным контекстом.
  • updaterэто объект, содержащий некоторые методы обновления,this.setStateПоследний звонокthis.updater.enqueueSetStateметод,this.forceUpdateПоследний звонокthis.updater.enqueueForceUpdateметод, поэтому эти API больше используются внутри React и доступны для нужд разработчиков.

В React, поскольку все компоненты класса наследуются отComponentкласс илиPureComponentкласс, так же, как метод написания собственного класса, его нужно написать вconstructorсначала позвониsuperметод полученияthis.

constructorЛучшей практикой для хуков жизненного цикла является инициализация здесьthis.state.

Конечно, вместо этого вы также можете использовать инициализаторы свойств, как показано ниже:

import React, { Component } from 'react';

class App extends Component {
    state = {
        name: 'biu',
    };
}

export default App;

componentWillMount()

💀 Это API устарело в React.

Это хук жизненного цикла до того, как компонент будет смонтирован в DOM.

У многих возникает непонимание: этот хук — лучшее время для запроса данных, а затем вставки данных в элемент и их монтирования вместе.

фактическиcomponentWillMountПричем крепление выполняется синхронно, а это значит, что после выполнения хука он монтируется сразу. Запрос данных с сервера выполняется асинхронно. Таким образом, каким бы быстрым ни был запрос, он должен быть обработан после задачи синхронизации, что является проблемой генерации.

То есть здесь никогда нельзя вставлять данные в элементы и монтировать их вместе.

Дело не в том, что здесь нельзя запросить данные, а в том, что вы не можете добиться того эффекта, который себе представляете.

Он устарел по двум основным причинам:

  • Это было бесполезно изначально. Предполагается, что он был создан с целью спаривания.
  • Если он объявляет таймер или подписчика при рендеринге на стороне сервера,componentWillUnmountКод очистки в хуках жизненного цикла не вступит в силу. Потому что, если компонент не смонтирован успешно,componentWillUnmountвыполняться не будет. Яо Мин сказал: «Если нет верховой езды, то нет и разъединения».
  • При асинхронном рендеринге он ведет себя хаотично.

инициализацияthis.stateдолжен быть вconstructorЗавершено в хуке жизненного цикла, данные запроса должны быть вcomponentDidMountКрючок жизненного цикла завершен, поэтому он не только заброшен, но и преемники.

static getDerivedStateFromProps(props, state)

👽 Это API, выпущенный React v16.3.0.

Во-первых, это хук жизненного цикла статического метода.

То есть при определении вы должны добавить метод перед методом.staticключевое слово или напрямую смонтировать его в классе class.

Кратко различайте методы экземпляра и статические методы:

  • метод экземпляра, смонтированный вthisна или установлен наprototypeВыше класс класса не может напрямую обращаться к этому методу, используйтеnewПосле создания экземпляра ключевого слова экземпляр может получить доступ к методу.
  • Статические методы, монтируемые непосредственно в класс класса или использующие новые ключевые словаstatic, экземпляр не может получить доступ к методу напрямую.

Вопрос в том, почемуgetDerivedStateFromPropsДолжны ли хуки жизненного цикла разрабатываться как статические методы?

Таким образом, разработчики не могут получить доступthisТо есть это экземпляр, и в нем нельзя вызывать методы экземпляра или setState.

import React, { Component } from 'react';

class App extends Component {
    render() {
        return (
            <div>React</div>
        );
    }

    static getDerivedStateFromProps(props, state) {}
}

export default App;

Миссия этого хука жизненного цикла состоит в том, чтобы обновлять свое собственное состояние по запросу на основе свойств родительского компонента.Это состояние называется производным состоянием. Возвращаемый объект — это состояние, которое будет постепенно обновляться.

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

Этот крючок жизненного цикла также претерпел некоторые повороты, он изначально был разработан, чтобы初始化,父组件更新а также接收到propsбудет запущен, и теперь он будет запускаться до тех пор, пока выполняется рендеринг, то есть初始化а также更新阶段сработает.

render()

Основной функцией компонента является монтирование элементов в DOM, поэтомуrenderКрючки жизненного цикла обязательно будут использоваться.

renderКак хуки жизненного цикла получают шаблоны? Конечно, вы вернетесь к нему.

Однако не рекомендуется писать слишком много логики перед возвратом, если логики слишком много, ее можно инкапсулировать в функцию.

render() {
    // 这里可以写一些逻辑
    return (
        <div>
            <input type="text" />
            <button>click</button>
        </div>
    );
}

Будьте осторожны, неrenderВызывается в хуках жизненного циклаthis.setState,потому чтоthis.setStateЭто вызовет рендер, и он никогда не закончится. Господи, есть предатель.

componentDidMount()

Это хук жизненного цикла после того, как компонент смонтирован в DOM.

Это может быть в дополнение кrenderСамый важный крючок жизненного цикла за пределами , потому что все аспекты компонента уже готовы, и мир зависит от вас.

Это социальный брат, здесь не так много жестоких слов.

componentWillReceiveProps(nextProps)

💀 Это API устарело в React.

componentWillReceivePropsХук жизненного цикла имеет только один параметр — update props.

Функция периода объявления может быть активирована в двух случаях:

  • Компонент получил новые свойства.
  • Компонент не получил новых свойств, но текущий компонент также был перерендерен из-за перерендеринга родителя.

Этот хук жизненного цикла не запускается во время инициализации.

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

shouldComponentUpdate(nextProps, nextState)

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

Существует исключение, если разработчик вызываетthis.forceUpdateПринудительное обновление, компоненты React будут игнорировать этот хук.

shouldComponentUpdateКрючки жизненного цикла по умолчанию возвращают true. То есть по умолчанию компонент всегда будет обновляться всякий раз, когда компонент инициирует обновление. React дает разработчику контроль над суждениями.

Но продуманный React также предоставляетPureComponentбазовый класс, аналогичныйComponentРазница между базовыми классамиPureComponentавтоматически реализуетshouldComponentUpdateКрючки жизненного цикла.

Для компонентов повторный рендеринг требуется только в случае изменения состояния. такshouldComponentUpdateКрючки жизненного цикла предоставляют два параметра, которые разработчики могут сравниватьthis.propsа такжеnextProps,this.stateа такжеnextStateЧтобы определить, изменилось ли состояние, а затем вернуть значение true или false соответственно.

При каких обстоятельствах состояние не меняется, но все же запускает обновление? Например:

Родительский компонент передает значение дочернему компоненту.При изменении состояния родительского компонента, даже если значение, полученное дочерним компонентом, не меняется, дочерний компонент будет принудительно обновлен. Это явно очень неразумно, React ничего не может с этим поделать, он может только смотреть на личное творение разработчика.

import React, { Component } from 'react';
import Child from './Child';

class App extends Component {
    state = { name: 'React', star: 1 };

    render() {
        const { name, star } = this.state;
        return (
            <div>
                <Child name={name} />
                <div>{star}</div>
                <button onClick={this.handle}>click</button>
            </div>
        );
    }

	handle = () => {
        this.setState(prevState => ({ star: ++prevState.star }));
    }
}

export default App;
import React, { Component } from 'react';

class Child extends Component {
    render() {
        return <h1>{this.props.name}</h1>;
    }

    shouldComponentUpdate(nextProps, nextState) {
        if (this.props === nextProps) {
            return false;
        } else {
            return true;
        }
    }
}

export default Child;

Также обратите внимание на ямы эталонных типов.

В следующем случаеthis.propsа такжеnextPropsникогда не может быть равным.

import React, { Component } from 'react';
import Child from './Child';

class App extends Component {
    state = { name: 'React', star: 1 };

    render() {
        return (
            <div>
                <Child name={{ friend: 'Vue' }} />
                <div>{this.state.star}</div>
                <button onClick={this.handle}>click</button>
            </div>
        );
    }

	handle = () => {
        this.setState(prevState => ({ star: ++prevState.star }));
    }
}

export default App;
import React, { Component } from 'react';

class Child extends Component {
    render() {
        return <h1>{this.props.friend}</h1>;
    }

    shouldComponentUpdate(nextProps, nextState) {
        if (this.props === nextProps) {
            return false;
        } else {
            return true;
        }
    }
}

export default Child;

Есть два обходных пути:

  • Сравниватьthis.props.xxxа такжеnextProps.xxx.
  • Кэшировать ссылочный тип в переменной родительского компонента.

такthis.stateа такжеnextStateЕго можно сравнить только с первым методом, потому что React будет возвращать новый объект каждый раз, когда обновляет состояние, вместо того, чтобы модифицировать исходный объект.

componentWillUpdate(nextProps, nextState)

💀 Это API устарело в React.

shouldComponentUpdateХук жизненного цикла возвращает true или вызываетthis.forceUpdateПосле этого хук жизненного цикла выполняется немедленно.

Обратите особое внимание,componentWillUpdateХук жизненного цикла будет выполняться перед каждым обновлением, поэтому вызовите его здесь.this.setStateЭто очень опасно и может быть бесконечным.

Кроме того, из-за введения механизма Fiber этот хук жизненного цикла может вызываться несколько раз.

getSnapshotBeforeUpdate(prevProps, prevState)

👽 Это API, выпущенный React v16.3.0.

Как следует из названия, он используется для сохранения моментальных снимков состояния.

Он будет вызван, когда компонент будет смонтирован, обратите внимание, что он вот-вот будет смонтирован. это даже звонит, чемrenderСлишком поздно, так что вы можете видетьrenderОперация монтирования не завершена, но выполнена работа по построению абстрактного пользовательского интерфейса.getSnapshotBeforeUpdateНемедленное выполнение вызоваcomponentDidUpdateКрючки жизненного цикла.

Для чего это? Есть некоторые состояния, такие как положение прокрутки веб-страницы, которые мне не нужно сохранять, мне просто нужно иметь возможность восстановить исходное положение после обновления компонента.

getSnapshotBeforeUpdateЗначение, возвращаемое хуком жизненного цикла, будетcomponentDidUpdateПолучен третий параметр, мы можем использовать этот канал для сохранения некоторых состояний, которые не нужно сохранять, и их можно отбросить, когда они израсходуются.

Очевидно, он используется для заменыcomponentWillUpdateКрючки жизненного цикла.

То есть разработчики обычно его не используют.

componentDidUpdate(nextProps, nextState, snapshot)

Это хук жизненного цикла, который срабатывает после обновления компонента.

соответствоватьgetSnapshotBeforeUpdateКогда используется хук жизненного цикла, третий параметрgetSnapshotBeforeUpdateВозвращаемое значение.

такой же,componentDidUpdateХук жизненного цикла будет выполняться после каждого обновления, поэтому вызовите его здесь.this.setStateЭто также очень опасно и может быть бесконечным.

componentWillUnmount()

Это хук жизненного цикла до размонтирования компонента.

Зачем нужен момент созерцания, когда компоненты вот-вот будут удалены?

Потому что разработчики хотят подтереть акридиновый зад.

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

Конечно, когда я говорю прослушиватели событий, я имею в виду следующее:

componentDidMount() {
    document.addEventListener('click', () => {});
}

Поскольку следующий React будет автоматически уничтожен, не беспокойте разработчиков.

render(
	return (
    	<button onClick={this.handle}>click</button>
    );
)

componentDidCatch(error, info)

👽 Это API, выпущенный React v16.3.0.

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

пользовательский только одинcomponentDidCatchкрючки жизненного циклаErrorBoundaryкомпонента, он делает одно: если обнаружена ошибка, показывает сообщение об ошибке, если ошибок нет, показывает дочерний компонент.

Поместите компонент, который должен поймать ошибку, какErrorBoundaryРендеринг дочернего компонента, как только дочерний компонент выдает ошибку, все приложение не падает, аErrorBoundaryзахватывать.

import React, { Component } from 'react';

class ErrorBoundary extends Component {
    state = { hasError: false };

    render() {
        if (this.state.hasError) {
            return <h1>Something went wrong.</h1>;
        }
        return this.props.children;
    }

    componentDidCatch(error, info) {
        this.setState({ hasError: true });
    }
}

export default ErrorBoundary;
import React from 'react';
import ErrorBoundary from './ErrorBoundary';
import MyWidget from './MyWidget';

const App = () => {
    return (
        <ErrorBoundary>
            <MyWidget />
        </ErrorBoundary>
    );
}

export default App;

Жизненный цикл

С таким количеством зацепок жизненного цикла на самом деле можно суммировать только три процесса:

  • устанавливать
  • возобновить
  • удалить

Монтирование и размонтирование выполняются только один раз, а обновления выполняются несколько раз.

Полный жизненный цикл компонента React будет последовательно вызывать следующие хуки:

old lifecycle

  • устанавливать

    • constructor
    • componentWillMount
    • render
    • componentDidMount
  • возобновить

    • componentWillReceiveProps
    • shouldComponentUpdate
    • componentWillUpdate
    • render
    • componentDidUpdate
  • удалить

    • componentWillUnmount

new lifecycle

  • устанавливать

    • constructor
    • getDerivedStateFromProps
    • render
    • componentDidMount
  • возобновить

    • getDerivedStateFromProps
    • shouldComponentUpdate
    • render
    • getSnapshotBeforeUpdate
    • componentDidUpdate
  • удалить

    • componentWillUnmount

Стек вызовов жизненного цикла дерева компонентов

Когда приложение впервые монтируется, мы начинаем сrenderа такжеcomponentDidMountНапример, React сначала вызовет корневой компонентrenderХуки, если есть подкомпоненты, вызывают подкомпоненты по очередиrenderХук, вызывающий процесс на самом деле представляет собой рекурсивную последовательность.

и т.д. всех компонентовrenderВсе хуки выполняются рекурсивно, в это время право на выполнение находится в руках последнего подкомпонента, поэтому запускается следующий раунд хуков жизненного цикла и вызывается последний подкомпонент.componentDidMountловушка, а затем стек вызовов рекурсивно вверх.

lifecycle stack

Стек вызовов жизненного цикла дерева компонентов имеет зигзагообразную форму.

Если корневой компонент не определяет обработчик жизненного цикла A, а подкомпонент определяет, то стек вызовов начинается с обработчика жизненного цикла A этого подкомпонента.

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

app.render();
child.render();
grandson.render();
// divide
grandson.componentDidMount();
child.componentDidMount();
app.componentDidMount();
// divide
app.render();
child.render();
grandson.render();
// divide
grandson.componentDidUpdate();
child.componentDidUpdate();
app.componentDidUpdate();

Конечно, хуки жизненного цикла componentWillMount, componentWillReceiveProps и componentWillUpdate могут прерываться или вызываться несколько раз, а производительность нестабильна. Поэтому React решил отказаться от них постепенно.

Однако понимание нормального порядка вызовов на протяжении жизненного цикла приложения полезно для понимания React.

Краткий обзор тем React

что такое пользовательский интерфейс

JSX

изменяемое состояние

Неизменные свойства

Жизненный цикл

компоненты

мероприятие

Манипулировать DOM

абстрактный пользовательский интерфейс