是否有系统的方法来调试导致组件在React中重新渲染的原因?我放置了一个简单的console.log()来查看它渲染了多少时间,但是在弄清楚是什么导致组件多次渲染(例如,我的情况是4次)时遇到了麻烦。是否存在显示时间轴和/或所有组件树渲染和顺序的工具?
是否有系统的方法来调试导致组件在React中重新渲染的原因?我放置了一个简单的console.log()来查看它渲染了多少时间,但是在弄清楚是什么导致组件多次渲染(例如,我的情况是4次)时遇到了麻烦。是否存在显示时间轴和/或所有组件树渲染和顺序的工具?
Answers:
如果您想要一个简短的代码段,而没有任何外部依赖性,那么我觉得这很有用
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>;
}
setState
方法(在类组件中),setState(...args) { super.setState(...args) }
然后在调试器中设置一个断点,这样您就可以追溯到功能设置状态。
useTraceUpdate
定义好您编写的代码后,我应该在哪里打电话?
function MyComponent(props) { useTraceUpdate(props); }
并且只要道具发生更改,它就会记录下来
this.state
未定义。
这是一些React组件将重新渲染的实例。
this.setState()
在组件内调用。这将触发以下组件的生命周期方法shouldComponentUpdate
> componentWillUpdate
>render
>componentDidUpdate
props
。这将触发componentWillReceiveProps
> shouldComponentUpdate
> componentWillUpdate
> render
> componentDidUpdate
(connect
的方法react-redux
触发该当存在在终极版存储适用的变化)this.forceUpdate
类似于this.setState
您可以通过在内部进行检查shouldComponentUpdate
并返回来最大程度地减少组件的重新渲染false
在不需要时。
另一种方法是使用React.PureComponent
或无状态的组件。纯的和无状态的组件仅在其道具发生更改时才重新渲染。
shouldComponentUpdate
或扩展React.PureComponent
来仅在更改时强制重新渲染。
const MyComponent = (props) => <h1>Hello {props.name}</h1>;
(这是一个无状态组件)。每当父组件重新渲染时,它将重新渲染。
@jpdelatorre的答案很好地强调了React组件可能重新呈现的一般原因。
我只是想更深入地研究一个实例:当道具改变时。对导致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个道具,通过将a 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)
}
将此片段添加到您的组件中可以帮助发现导致可疑重新渲染的罪魁祸首,而且很多时候,这有助于阐明不必要的数据被管道传输到组件中的情况。
UNSAFE_componentWillReceiveProps(nextProps)
它,不推荐使用。“此生命周期以前已命名componentWillReceiveProps
。该名称将继续使用到版本17。” 来自React文档。
奇怪的是没有人给出这个答案,但是我发现它非常有用,特别是因为道具更改几乎总是嵌套在深处。
勾子迷们:
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);
}
}
PS我仍然喜欢使用HOC(高阶分量),因为有时您在顶部分解了道具,而Jacob的解决方案不太适合
免责声明:与包所有者没有任何隶属关系。只需单击数十次以尝试发现深层嵌套对象中的差异是很痛苦的。
使用挂钩和功能组件,不仅更改道具也可能导致重新渲染。我开始使用的是相当手工的日志。我帮了我很多 您可能也会发现它很有用。
我将此部分粘贴到组件的文件中:
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()
在要测试的组件的render函数中放置一个。
然后您可以运行您的应用并检查控制台日志。只要有日志,它就会显示状态之间的差异(nextProps and this.props)
,您可以决定是否真的需要渲染
它将与diff键相似于上图。
shouldComponentUpdate
用来禁用组件自动更新,然后从那里开始跟踪。更多信息可以在这里找到:facebook.github.io/react/docs/optimizing-performance.html