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.