1. Несколько проблем, часто встречающихся при разработке
Следующие проблемы представляют собой сценарии, с которыми мы часто сталкиваемся в реальной разработке. Давайте восстановим их с помощью нескольких простых примеров кода.
1. Является ли setState синхронным или асинхронным, почему я не могу сразу получить результат обновления, но иногда могу?
1.1 Хуки-функции и синтетические события ReactsetState
Теперь есть два компонента
componentDidMount() {
console.log('parent componentDidMount');
}
render() {
return (
<div>
<SetState2></SetState2>
<SetState></SetState>
</div>
);
}
Поместите тот же код внутри компонента и поместите его вSetstate1
серединаcomponentDidMount
В разделе кода размещена задержка синхронизации, время задержки печати:
componentWillUpdate() {
console.log('componentWillUpdate');
}
componentDidUpdate() {
console.log('componentDidUpdate');
}
componentDidMount() {
console.log('SetState调用setState');
this.setState({
index: this.state.index + 1
})
console.log('state', this.state.index);
console.log('SetState调用setState');
this.setState({
index: this.state.index + 1
})
console.log('state', this.state.index);
}
Результат выполнения следующий:
инструкция:
- 1. звонок
setState
не будет обновляться сразу - 2. Все компоненты используют один и тот же механизм обновления, когда все компоненты
didmount
После родительского компонентаdidmount
И выполнить обновление - 3. При обновлении обновления каждого компонента будут объединены, и каждый компонент запустит жизненный цикл обновления только один раз.
1.2 Асинхронные функции и нативные событияsetstate
?
существуетsetTimeout
вызыватьsetState
(Пример такой же, как выполнение в браузере собственных событий и обратных вызовов интерфейса)
componentDidMount() {
setTimeout(() => {
console.log('调用setState');
this.setState({
index: this.state.index + 1
})
console.log('state', this.state.index);
console.log('调用setState');
this.setState({
index: this.state.index + 1
})
console.log('state', this.state.index);
}, 0);
}
Результаты:
инструкция:
- 1. В родительском компоненте
didmount
после казни - 2. позвонить
setState
Обновление синхронизации
2. Почему иногда два раза подрядsetState
Работает только один раз?
Выполните следующие коды соответственно:
componentDidMount() {
this.setState({ index: this.state.index + 1 }, () => {
console.log(this.state.index);
})
this.setState({ index: this.state.index + 1 }, () => {
console.log(this.state.index);
})
}
componentDidMount() {
this.setState((preState) => ({ index: preState.index + 1 }), () => {
console.log(this.state.index);
})
this.setState(preState => ({ index: preState.index + 1 }), () => {
console.log(this.state.index);
})
}
Результаты:
1
1
2
2
инструкция:
- 1. Передайте объект напрямую
setstate
будут объединены в один - 2. Используйте проход функции
state
не будет объединен
Два .SetState Исполнение
Поскольку исходный код более сложный, он не будет размещен здесь, если вам интересно, вы можете перейти наgithub
начальствоclone
Одна копия, а затем просмотрите ее в соответствии с приведенной ниже блок-схемой.
1. Блок-схема
Картинка не четкая, можно нажать
-
partialState
:setState
Первый переданный параметр, объект или функция -
_pendingStateQueue
: текущий компонент ожидает выполнения обновленияstate
очередь -
isBatchingUpdates
: реакция используется, чтобы определить, находится ли он в настоящее время в состоянии пакетного обновления, общего для всех компонентов. -
dirtyComponent
: Очереди всех компонентов, находящихся в настоящее время в состоянии ожидания обновления. -
transcation
: механизм транзакций React, оборачивающий n методов вокруг метода, вызываемого транзакцией.waper
объект и выполнить его один раз:waper.init
, вызываемый метод,waper.close
-
FLUSH_BATCHED_UPDATES
: используется для выполнения обновленияwaper
,только одинclose
метод
2. Процесс исполнения
Согласно текстовому описанию приведенной выше блок-схемы, ее можно условно разделить на следующие этапы:
- 1. Передайте в setState
partialState
Параметры хранятся в промежуточной очереди состояния текущего экземпляра компонента. - 2. Определите, находится ли текущий React в состоянии пакетного обновления, и если да, добавьте текущий компонент в очередь компонентов для обновления.
- 3. Если он не находится в состоянии пакетного обновления, установите для флага состояния пакетного обновления значение true и используйте транзакцию для повторного вызова предыдущего метода, чтобы убедиться, что текущий компонент добавлен в очередь компонентов для обновления.
- 4. Вызвать транзакцию
waper
метод, пройдите по очереди компонентов, которые необходимо обновить, и выполните обновления последовательно. - 5. Жизненный цикл исполнения
componentWillReceiveProps
. - 6. Временно сохранить состояние компонента в очереди
state
Слияние, получение объекта конечного состояния для обновления и очистка очереди. - 7. Жизненный цикл исполнения
componentShouldUpdate
, в соответствии с возвращаемым значением, чтобы определить, следует ли продолжать обновление. - 8. Жизненный цикл исполнения
componentWillUpdate
. - 9. Выполните настоящее обновление,
render
. - 10. Жизненный цикл выполнения
componentDidUpdate
.
3. Резюме
1. В хуковых функциях и синтетических событиях:
существуетreact
в жизненном цикле и синтетических событиях,react
все еще в своем механизме обновления, в это времяisBranchUpdate
правда.
В соответствии с описанным выше процессом, независимо от того, сколько раз вы звонитеsetState
, не будет выполнять обновление, но обновитstate
депозит_pendingStateQueue
, сохраните компонент, который нужно обновить, вdirtyComponent
.
При выполнении последнего механизма обновления, на примере жизненного цикла, все компоненты, то есть компонент верхнего уровняdidmount
позжеisBranchUpdate
Установите значение «ложь». В это время накопленный ранееsetState
.
2. В асинхронных функциях и нативных событиях
С точки зрения исполнительного механизма,setState
не является асинхронным как таковым, но при вызовеsetState
когда, еслиreact
В процессе обновления текущее обновление будет временно сохранено и будет выполнено после выполнения последнего обновления, что создает иллюзию асинхронности.
В жизненном цикле, согласно асинхронному механизму JS, асинхронная функция будет временно храниться, и она будет выполняться после выполнения всех синхронных кодов, в это время был выполнен последний процесс обновления.isBranchUpdate
установлено значение false в соответствии с описанным выше процессом, затем снова вызовитеsetState
Вы можете выполнить обновление немедленно и получить результат обновления.
3.partialState
Механизм слияния
Давайте посмотрим на процесс_processPendingState
код, эта функция используется для слиянияstate
Временная очередь и, наконец, вернуть объединенныйstate
.
_processPendingState: function (props, context) {
var inst = this._instance;
var queue = this._pendingStateQueue;
var replace = this._pendingReplaceState;
this._pendingReplaceState = false;
this._pendingStateQueue = null;
if (!queue) {
return inst.state;
}
if (replace && queue.length === 1) {
return queue[0];
}
var nextState = _assign({}, replace ? queue[0] : inst.state);
for (var i = replace ? 1 : 0; i < queue.length; i++) {
var partial = queue[i];
_assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);
}
return nextState;
},
Нам просто нужно сосредоточиться на следующем коде:
_assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);
Если объект передается, он, очевидно, будет объединен один раз:
Object.assign(
nextState,
{index: state.index+ 1},
{index: state.index+ 1}
)
Если передается функция, параметр preState функции является результатом предыдущего слияния, поэтому результат вычисления является точным.
4.componentDidMount
перечислитьsetstate
В componentDidMount() вы можете немедленно вызвать setState(). Это вызовет дополнительный рендеринг, но это произойдет до того, как браузер обновит экран. Это гарантирует, что пользователь не увидит промежуточное состояние, даже если в этом случае функция render() будет вызываться дважды. Используйте этот режим с осторожностью, так как он часто вызывает проблемы с производительностью. В большинстве случаев вместо этого вы можете использовать начальное состояние присваивания в конструкторе(). Однако бывают ситуации, когда это необходимо, например, для модальных окон и всплывающих подсказок. На этом этапе вам нужно измерить эти узлы DOM, прежде чем отображать что-то, что зависит от размера или положения.
Выше приведено описание официального документа, не рекомендуется использоватьcomponentDidMount
позвонить напрямуюsetState
, согласно приведенному выше анализу:componentDidMount
сам в обновлении, звоним сноваsetState
, сделаю это снова в будущемrender
, вызывая ненужные потери производительности, в большинстве случаев это можно сделать, установив начальное значение.
Конечно вcomponentDidMount
Мы можем вызвать интерфейс и изменить его в обратном вызовеstate
, это правильный подход.
Когда начальное значение состояния зависит от атрибута dom, вcomponentDidMount
серединаsetState
неизбежно.
5.componentWillUpdate
componentDidUpdate
Эти два жизненных цикла нельзя назватьsetState
.
Это легко найти из приведенной выше блок-схемы, в которой они вызываютsetState
Это вызовет бесконечный цикл и приведет к сбою программы.
6. Рекомендуемое использование
вызовsetState
При использовании функции passstate
значение, получить последнее обновленное значение в функции обратного вызоваstate
.