【发布时间】:2019-10-23 08:26:27
【问题描述】:
我做了一个演示,显示了我的问题。在我的实际情况中,我有更复杂的数据结构,并且有更多需要它的理由。
原因 - 我制作了一个非常通用的组件包装器(示例中为App)。消费者程序员可以提供自己的方式,用于如何在我的组件中显示内容。他必须提供一个函数,该函数决定何时渲染什么(以App 和myComponentSwitch() 上的componentSwitch 属性为例)。他提供的所有组件都必须具有继承特定接口的道具(示例中为IItemBaseProps)。当然,也有一些情况,他需要为组件提供更多的 props。在那种情况下,我这样编写组件开关:
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"}
我有点理解,为什么它会这样。我想知道 - 这是一个性能漏洞吗?我猜想,更新组件会比每次更新都制作新组件便宜。我该怎么做才能让这些组件只获得更新?
【问题讨论】:
标签: javascript reactjs typescript