Реагируйте: родительский компонент повторно отображает все дочерние элементы, даже те, которые не изменились при изменении состояния
Я не смог найти четкого ответа на этот вопрос, надеюсь, это не повторяется.
Я использую React + Redux для простого приложения чата. Приложение состоит из компонентов InputBar, MessageList и Container. Контейнер (как вы можете себе представить) упаковывает два других компонента и подключается к хранилищу. Состояние моих сообщений, а также текущее сообщение (сообщение, которое пользователь в данный момент печатает) хранится в хранилище Redux. Упрощенная структура:
class ContainerComponent extends Component {
...
render() {
return (
<div id="message-container">
<MessageList
messages={this.props.messages}
/>
<InputBar
currentMessage={this.props.currentMessage}
updateMessage={this.props.updateMessage}
onSubmit={this.props.addMessage}
/>
</div>
);
}
}
У меня возникает проблема при обновлении текущего сообщения. Обновление текущего сообщения запускает действие, которое обновляет хранилище, которое обновляет реквизиты, проходящие через контейнер и обратно в компонент InputBar.
Это работает, однако побочным эффектом является то, что мой компонент MessageList перерисовывается каждый раз, когда это происходит. MessageList не получает текущее сообщение и не имеет причин для обновления. Это большая проблема, потому что как только MessageList становится большим, приложение становится заметно медленнее каждый раз, когда текущие обновления сообщений.
Я попытался установить и обновить текущее состояние сообщения непосредственно в компоненте InputBar (полностью игнорируя архитектуру Redux), и это "решает" проблему, однако я хотел бы придерживаться шаблона проектирования Redux, если это возможно.
Мои вопросы:
Если родительский компонент обновляется, всегда ли React обновляет все прямые дочерние элементы в этом компоненте?
Какой правильный подход здесь?
1 ответ
Если родительский компонент обновляется, всегда ли React обновляет все прямые дочерние элементы в этом компоненте?
Нет. React будет перерисовывать компонент только в том случае, если shouldComponentUpdate()
возвращается true
, По умолчанию этот метод всегда возвращает true
чтобы избежать каких-либо тонких ошибок для новичков (и, как отметил Уильям Б., DOM на самом деле не будет обновляться, если что-то не изменится, что снизит влияние).
Чтобы предотвратить ненужный повторный рендеринг вашего подкомпонента, вам необходимо реализовать shouldComponentUpdate
метод таким образом, что он только возвращает true
когда данные действительно изменились. Если this.props.messages
всегда один и тот же массив, это может быть так просто:
shouldComponentUpdate(nextProps) {
return (this.props.messages !== nextProps.messages);
}
Вы также можете захотеть сделать какое-то глубокое сравнение или сравнение идентификаторов сообщений или что-то еще, это зависит от ваших требований.
Если обновляется родительский компонент, всегда ли React обновляет всех прямых потомков в этом компоненте? -> Да, по умолчанию, если родитель изменяет, все его прямые дочерние элементы повторно отображаются, но этот повторный рендеринг не обязательно изменяет фактический DOM, именно так работает React, только видимые изменения обновляются до реального DOM.
Какой здесь правильный подход? -> Чтобы предотвратить даже повторный рендеринг виртуальной DOM, чтобы повысить производительность, вы можете использовать любой из следующих методов:
Применить метод жизненного цикла ShouldComponentUpdate - применяется только в том случае, если ваш дочерний компонент основан на классе, вам нужно проверить текущее значение props с предыдущим значением props, и если они верны, просто верните false.
Use Pure Component -> Это просто более короткая версия вышеупомянутого метода, снова работает с компонентами на основе классов
Используйте React memo -> это лучший способ предотвратить повторную визуализацию, даже если у вас есть функциональные компоненты, вам просто нужно обернуть экспорт компонентов с помощью React.memo, например:
export default React.memo(MessageList)
Надеюсь, это поможет!
Если реквизиты родительского компонента изменились, он будет перерисовывать все свои дочерние элементы, которые сделаны с использованием React.Component
заявление.
Попробуйте сделать свой <MessageList>
компонент а React.PureComponent
чтобы избежать этого.
Согласно документам React: In the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false may still result in a re-rendering of the component.
проверьте эту ссылку для получения дополнительной информации
Надеюсь, что это поможет любому, кто ищет правильный способ исправить это.
Если вы используете
map
для рендеринга дочерних компонентов и использования для них уникального ключа (что-то вроде
uuid()
), возможно, вернитесь к использованию
i
с карты в качестве ключа. Это может решить проблему повторного рендеринга.
Не уверен в этом подходе, но иногда он решает проблему