[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} />
또는 MyComponent
redux 상점에 연결된 경우 :
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 키와 함께 위 이미지와 비슷합니다.