[reactjs] React 컴포넌트가 다시 렌더링되는 이유 추적

React에서 구성 요소가 다시 렌더링되는 원인을 디버깅하는 체계적인 접근 방법이 있습니까? 간단한 console.log ()를 넣어 얼마나 많은 시간을 렌더링하는지 알 수 있지만 구성 요소가 여러 번 (4 번) 렌더링되는 원인을 파악하는 데 문제가 있습니다. 타임 라인 및 / 또는 모든 구성 요소 트리 렌더링 및 순서를 표시하는 도구가 있습니까?



답변

외부 의존성이없는 짧은 스 니펫을 원한다면 이것이 유용하다고 생각합니다.

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>;
}


답변

다음은 React 컴포넌트가 다시 렌더링하는 인스턴스입니다.

  • 부모 컴포넌트 렌더링
  • this.setState()컴포넌트 내에서 호출 이것은 다음과 같은 구성 요소 수명주기 방법 트리거 shouldComponentUpdate> componentWillUpdate> render>을componentDidUpdate
  • 구성 요소의 변화 props. 이 의지 트리거 componentWillReceiveProps> shouldComponentUpdate> componentWillUpdate> render> componentDidUpdate( connect방법 react-redux트리거 이것을 돌아 오는 저장소에 해당하는 변경 사항이있을 경우)
  • 다음 this.forceUpdate과 유사한 호출this.setState

내부 검사를 구현하고 필요하지 않은 경우 shouldComponentUpdate반환 하여 구성 요소의 렌더링을 최소화 할 수 있습니다 false.

다른 방법은 상태 비 저장 구성 요소 를 사용하는 것입니다 React.PureComponent . 순수 및 상태 비 저장 구성 요소는 소품에 변경 사항이있는 경우에만 다시 렌더링합니다.


답변

@jpdelatorre의 답변은 React 컴포넌트가 다시 렌더링되는 일반적인 이유를 강조하는 데 유용합니다.

방금 소품이 바뀌는 순간에 대해 조금 더 깊이 파고 들었습니다 . React 컴포넌트가 다시 렌더링되는 원인을 해결하는 것은 일반적인 문제이며, 필자의 경험상 이 문제를 추적 하는 데 많은 시간 이 걸리는 소품을 결정하는 것이 필요 합니다.

새로운 소품을받을 때마다 반응 구성 요소가 다시 렌더링됩니다. 그들은 다음과 같은 새로운 소품을받을 수 있습니다.

<MyComponent prop1={currentPosition} prop2={myVariable} />

또는 MyComponentredux 상점에 연결된 경우 :

function mapStateToProps (state) {
  return {
    prop3: state.data.get('savedName'),
    prop4: state.data.get('userCount')
  }
}

언제의 값은 prop1, prop2, prop3, 또는 prop4변화는 MyComponent다시 렌더링됩니다. 4 개의 소품 console.log(this.props)으로, render블록의 시작 부분 에을 넣어 어떤 소품이 바뀌고 있는지 추적하는 것은 그리 어렵지 않습니다 . 그러나 더 복잡한 구성 요소와 점점 더 많은 소품 으로이 방법을 사용할 수 없습니다.

다음은 편의를 위해 lodash 를 사용하여 구성 요소를 다시 렌더링하는 소품 변경 사항을 확인 하는 유용한 방법입니다 .

componentWillReceiveProps (nextProps) {
  const changedProps = _.reduce(this.props, function (result, value, key) {
    return _.isEqual(value, nextProps[key])
      ? result
      : result.concat(key)
  }, [])
  console.log('changedProps: ', changedProps)
}

이 스 니펫을 구성 요소에 추가하면 의심스러운 재 렌더링의 원인이되는 범인을 밝히는 데 도움이되며, 여러 번 구성 요소에 파이프되는 불필요한 데이터를 밝히는 데 도움이됩니다.


답변

이상한 대답은 없었지만 특히 소품 변경이 거의 항상 깊게 중첩되어 있기 때문에 매우 유용합니다.

후크 팬보이 :

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} />;
  };
};

“오래된”학교 팬보이 :

import deep_diff from "deep-diff";
componentDidUpdate(prevProps, prevState) {
      const diff = deep_diff.diff(prevProps, this.props);
      if (diff) {
        console.log(diff);
      }
}

추신 : 나는 여전히 최고의 소품을 구조화하고 Jacob의 솔루션이 적합하지 않기 때문에 HOC (higher order component)를 사용하는 것을 선호합니다.

면책 조항 : 패키지 소유자와 아무런 관련이 없습니다. 깊게 중첩 된 객체의 차이점을 발견하기 위해 수십 번 클릭하면 고통이 있습니다.


답변

npm에서 사용할 수있는 후크가 있습니다.

https://www.npmjs.com/package/use-trace-update

(공개, 게시) 업데이트 : Jacob Rask의 코드를 기반으로 개발


답변

소품 변경뿐만 아니라 후크와 기능적 구성 요소를 사용하면 다시 렌더링 할 수 있습니다. 내가 사용하기 시작한 것은 수동 로그입니다. 나는 많은 도움을 주었다. 유용 할 수도 있습니다.

이 부분을 구성 요소 파일에 붙여 넣습니다.

const keys = {};
const checkDep = (map, key, ref, extra) => {
  if (keys[key] === undefined) {
    keys[key] = {key: key};
    return;
  }
  const stored = map.current.get(keys[key]);

  if (stored === undefined) {
    map.current.set(keys[key], ref);
  } else if (ref !== stored) {
    console.log(
      'Ref ' + keys[key].key + ' changed',
      extra ?? '',
      JSON.stringify({stored}).substring(0, 45),
      JSON.stringify({now: ref}).substring(0, 45),
    );
    map.current.set(keys[key], ref);
  }
};

메소드의 시작 부분에서 WeakMap 참조를 유지합니다.

const refs = useRef(new WeakMap());

그런 다음 각 “의심스러운”호출 (소품, 갈고리) 후에 다음과 같이 씁니다.

const example = useExampleHook();
checkDep(refs, 'example ', example);


답변

위의 답변은 누군가가 렌더링의 원인을 감지하는 특별한 방법을 찾고 있다면 이 라이브러리 redux-logger가 매우 도움이되는 경우를 대비하여 매우 유용합니다.

할 수있는 일은 라이브러리를 추가하고 다음과 같이 상태 (문서에 있음) 사이의 차이점을 활성화하는 것입니다.

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

그리고 상점에 미들웨어를 추가하십시오.

그런 다음 console.log()테스트하려는 구성 요소의 렌더링 기능 에을 넣으십시오 .

그런 다음 앱을 실행하고 콘솔 로그를 확인할 수 있습니다. (nextProps and this.props) 하고 렌더링이 실제로 필요한지 여부를 결정할 수 있습니다여기에 이미지 설명을 입력하십시오

diff 키와 함께 위 이미지와 비슷합니다.