javascript - React 在功能组件中的组件的每次更新时调用构造函数
问题描述
我做了一个演示,显示了我的问题。在我的实际情况中,我有更复杂的数据结构,以及我需要它的更多理由。
原因 - 我制作了一个非常通用的组件包装器(App
在示例中)。消费者程序员可以提供自己的方式,用于如何在我的组件中显示内容。他必须提供一个函数,该函数决定 - 何时渲染什么(以道具componentSwitch
为例)。他提供的所有组件都必须具有继承特定接口的道具(在示例中)。当然,在某些情况下,他需要为组件提供更多的 props。在这种情况下,我这样编写组件开关:App
myComponentSwitch()
IItemBaseProps
c = (props: IItemBaseProps) => <TypeB {...props} color="red" />;
这实现了我想要的 -c
将成为一个将IItemBaseProps
作为道具的组件。但它会返回TypeB
带有附加属性的组件color
。
出现的问题 - 每次呈现此功能组件时,TypeB
都会调用构造函数。componentDidUpdate
永远不会被调用。
我制作了一个示例应用程序。这是代码:
import React, { Component } from "react";
import { render } from "react-dom";
interface IItemBaseProps {
value: string;
}
interface IItemAProps extends IItemBaseProps {}
interface IItemBProps extends IItemBaseProps {
color: string;
}
interface IItem {
type: "A" | "B";
props: IItemBaseProps;
}
class TypeA extends Component<IItemAProps> {
constructor(props: IItemAProps) {
super(props);
console.log("TypeA::constructor", props);
}
componentDidUpdate(props: IItemAProps) {
console.log("TypeA::componentDidUpdate", props, this.props);
}
render() {
return (
<div>
<b>{this.props.value}</b>
</div>
);
}
}
class TypeB extends Component<IItemBProps> {
constructor(props: IItemBProps) {
super(props);
console.log("TypeB::constructor", props);
}
componentDidUpdate(props: IItemBProps) {
console.log("TypeB::componentDidUpdate", props, this.props);
}
render() {
return (
<div>
<u style={{ color: this.props.color }}>{this.props.value}</u>
</div>
);
}
}
interface IITemWrapperProps {
props: IItemBaseProps;
component: React.ComponentType<IItemBaseProps>;
}
class ItemWrapper extends Component<IITemWrapperProps> {
render() {
return <this.props.component {...this.props.props} />;
}
}
interface AppProps {
componentSwitch: (type: "A" | "B") => React.ComponentType<IItemBaseProps>;
}
interface AppState {
items: IItem[];
}
class App extends Component<AppProps, AppState> {
constructor(props) {
super(props);
this.state = {
items: [
{
type: "A",
props: {
value: "Value A1"
}
},
{
type: "A",
props: {
value: "Value A2"
}
},
{
type: "B",
props: {
value: "Value B1"
}
},
{
type: "ERR",
props: {
value: "Erroring item"
}
}
]
};
}
onClick = () => {
console.log("---- click");
this.setState(state => {
return {
...state,
items: state.items.map((item, i) => {
if (i === 0) {
console.log("Updating items[" + i + "], type: " + item.type);
return {
...item,
props: {
...item.props,
value: item.props.value + "!"
}
};
}
return item;
})
};
});
};
render() {
return (
<div>
<div>
{this.state.items.map((item, i) => {
return <ItemWrapper key={i} component={this.props.componentSwitch(item.type)} props={item.props} />;
})}
</div>
<div>
<button onClick={this.onClick}>Update first item!</button>
</div>
</div>
);
}
}
function myComponentSwitch(
type: "A" | "B"
): React.ComponentType<IItemBaseProps> {
if (type === "A") {
return TypeA;
}
if (type === "B") {
return (props: IItemBaseProps) => <TypeB {...props} color="red" />;
}
return (props: IItemBaseProps) => (
<div>
<i>{props.value}</i>
</div>
);
}
render(<App componentSwitch={myComponentSwitch}/>,
document.getElementById("root"));
而且,你可以在这里演示: https ://stackblitz.com/edit/react-ts-a95cd6
有一个按钮,用于更新第一项。控制台中的输出:
TypeA::constructor {value: "Value A1"}
TypeA::constructor {value: "Value A2"}
TypeB::constructor {value: "Value B1", color: "red"} // So far so good, 1st render
---- click // event on click
Updating items[0], type: A
TypeB::constructor {value: "Value B1", color: "red"} // TypeB is constructed again as a new instance
TypeA::componentDidUpdate {value: "Value A1"} {value: "Value A1!"} // TypeA components are just updated
TypeA::componentDidUpdate {value: "Value A2"} {value: "Value A2"}
我有点理解,为什么它会这样。我想知道 - 这是一个性能漏洞吗?我猜想,更新组件会比每次更新都制作新组件便宜。我该怎么做,以便这些组件只获得更新?
解决方案
每次调用 setState 时,组件都会保存该状态,并且他的所有孩子都会获得一个完全重新实例化的循环。
如果您想优化循环,您可以重新修改应用程序以将状态部分声明到应该真正重新渲染的位置。
或者使用一个全局状态解决方案(redux、easy-peasy ...)
但是 React 团队移动和提升的方式是组件中的碎片状态,并使用上下文来获得更多可共享的状态。
推荐阅读
- python - tensorflow.keras.models 遇到错误
- c# - C#如何从项目根目录下的文件夹中读取文件
- python - 有没有办法创建一个与变量同名的文件夹,然后在 python 中放置一个 JSON 文件?
- html - 有人可以帮我在这个 div 上对齐 X 吗?我一直在玩它,无法弄清楚:(
- java - 将字符串电话号码转换为长:数字格式异常
- python - strptime 不更改 Excel 中的日期格式
- ios - 如何使用按钮获取 collectionView 的 indexPathForVisableSections?
- python - 存储图像特征向量的最有效方法是什么?
- jhipster - jhipster 应用程序无法运行 - 没有明显的错误
- r - 我正在处理 DTM,我想做 k-means、heirarchical 和 k-medoids 聚类。我想先规范化 DTM 吗?