Отслеживайте, почему компонент React перепродает
Существует ли системный подход к отладке того, что вызывает повторную визуализацию компонента в React? Я поместил простой console.log(), чтобы увидеть, сколько раз он рендерится, но у меня возникают проблемы с выяснением того, что вызывает рендеринг компонента несколько раз, т.е. (4 раза) в моем случае. Существует ли инструмент, который отображает временную шкалу и/или все компоненты дерева отображает и порядок?
Ответы
Ответ 1
Если вам нужен короткий фрагмент без каких-либо внешних зависимостей, я считаю это полезным
componentDidUpdate(prevProps, prevState) {
Object.entries(this.props).forEach(([key, val]) =>
prevProps[key] !== val && console.log('Prop '${key}' changed')
);
if (this.state) {
Object.entries(this.state).forEach(([key, val]) =>
prevState[key] !== val && console.log('State '${key}' changed')
);
}
}
Вот небольшой хук, который я использую для отслеживания обновлений компонентов функций
function useTraceUpdate(props) {
const prev = useRef(props);
useEffect(() => {
const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
if (prev.current[k] !== v) {
ps[k] = [prev.current[k], v];
}
return ps;
}, {});
if (Object.keys(changedProps).length > 0) {
console.log('Changed props:', changedProps);
}
prev.current = props;
});
}
// Usage
function MyComponent(props) {
useTraceUpdate(props);
return <div>{props.children}</div>;
}
Ответ 2
Вот некоторые примеры, которые компонент React будет повторно отображать.
- Родительский компонент rerender
- Вызов
this.setState()
внутри компонента. Это приведет к следующим методам жизненного цикла компонентов shouldComponentUpdate
> componentWillUpdate
> render
> componentDidUpdate
- Изменения в
props
компонентов. Это вызовет componentWillReceiveProps
> shouldComponentUpdate
> componentWillUpdate
> render
> componentDidUpdate
(метод connect
для react-redux
запускает это при наличии соответствующих изменений в хранилище Redux) - вызов
this.forceUpdate
который похож на this.setState
Вы можете свести к минимуму свой компонентный рендер, выполнив проверку внутри вашего shouldComponentUpdate
и вернув false
если это не нужно.
Другой способ - использовать компоненты React.PureComponent
или React.PureComponent
. Чистые и безгосударственные компоненты только повторно отображают, когда есть изменения в реквизитах.
Ответ 3
Ответ @jpdelatorre отлично подходит для выделения общих причин, по которым компонент React может повторно отображаться.
Я просто хотел немного погрузиться в один случай: когда реквизит изменится. Устранение неполадок, связанных с повторной обработкой компонента React, является распространенной проблемой, и, по моему опыту, многие моменты, отслеживающие эту проблему, включают в себя определение того, какие реквизиты меняются.
Реанимация компонентов повторно производится всякий раз, когда они получают новые реквизиты. Они могут получать новые реквизиты, такие как:
<MyComponent prop1={currentPosition} prop2={myVariable}/>
или если MyComponent
подключен к магазину redux:
function mapStateToProps (state) {
return {
prop3: state.data.get('savedName'),
prop4: state.data.get('userCount')
}
}
prop1
prop2
, prop3
значение prop1
, prop2
, prop3
или prop4
изменяется, MyComponent
будет повторно отображать. С 4 реквизитом не так сложно отследить, какие реквизиты меняются, помещая console.log(this.props)
в начале блока render
. Однако с более сложными компонентами и большим количеством реквизитов этот метод несостоятелен.
Вот полезный подход (с помощью lodash для удобства), чтобы определить, какие изменения prop приводят к повторному рендерингу компонента:
componentWillReceiveProps (nextProps) {
const changedProps = _.reduce(this.props, function (result, value, key) {
return _.isEqual(value, nextProps[key])
? result
: result.concat(key)
}, [])
console.log('changedProps: ', changedProps)
}
Добавление этого фрагмента к вашему компоненту может помочь выявить виновника, вызывающего сомнительные повторные визуализации, и много раз это помогает пролить свет на ненужные данные, передаваемые по компонентам.
Ответ 4
Там теперь крючок для этого доступен на npm:
https://www.npmjs.com/package/use-trace-update
(Раскрытие, я это опубликовал)
Ответ 5
Вышеприведенные ответы очень полезны, на всякий случай, если кто-то ищет специальный метод для обнаружения причины rerender, тогда я нашел эту библиотеку redux-logger очень полезной.
Что вы можете сделать, это добавить библиотеку и включить различие между состоянием (оно есть в документах), например:
const logger = createLogger({
diff: true,
});
И добавьте промежуточное ПО в магазин.
Затем поставьте console.log()
в функцию рендеринга компонента, который вы хотите протестировать.
Затем вы можете запустить свое приложение и проверить консольные журналы. Если есть журнал, перед тем, как он покажет вам разницу между состояниями (nextProps and this.props)
и вы можете решить, действительно ли там нужен рендеринг
Он будет похож на изображение выше вместе с ключом diff.
Ответ 6
Странно, что никто не дал такого ответа, но я считаю его очень полезным, тем более что изменения в подпорках почти всегда глубоко вложены.
Крючки фанатов:
import deep_diff from "deep-diff";
const withPropsChecker = WrappedComponent => {
return props => {
const prevProps = useRef(props);
useEffect(() => {
const diff = deep_diff.diff(prevProps.current, props);
if (diff) {
console.log(diff);
}
prevProps.current = props;
});
return <WrappedComponent {...props} />;
};
};
"Старые" -school фанаты:
import deep_diff from "deep-diff";
componentDidUpdate(prevProps, prevState) {
const diff = deep_diff.diff(prevProps, this.props);
if (diff) {
console.log(diff);
}
}
PS Я все еще предпочитаю использовать HOC (компонент более высокого порядка), потому что иногда вы деструктурируете свои реквизиты сверху, и решение Jacob не подходит
Отказ от ответственности: Нет связи с владельцем пакета. Просто нажимать десятки раз вокруг, чтобы попытаться определить разницу в глубоко вложенных объектах, - это боль в.