Отслеживайте, почему компонент 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)
}

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

Ответ 5

Вышеприведенные ответы очень полезны, на всякий случай, если кто-то ищет специальный метод для обнаружения причины rerender, тогда я нашел эту библиотеку redux-logger очень полезной.

Что вы можете сделать, это добавить библиотеку и включить различие между состоянием (оно есть в документах), например:

const logger = createLogger({
    diff: true,
});

И добавьте промежуточное ПО в магазин.

Затем поставьте console.log() в функцию рендеринга компонента, который вы хотите протестировать.

Затем вы можете запустить свое приложение и проверить консольные журналы. Если есть журнал, перед тем, как он покажет вам разницу между состояниями (nextProps and this.props) и вы можете решить, действительно ли там нужен рендеринг enter image description here

Он будет похож на изображение выше вместе с ключом 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 не подходит

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