ReactJS:映射组件的构造函数被调用了多少次?
ReactJS: How many times is a mapped component's constructor called?
我有一个 React 组件,它在另一个组件中使用 Array.prototype.map() 多次渲染。
第一个组件的构造函数应该被调用多少次?我期待与映射数组的长度一样多的次数,但它似乎只被调用了一次。
在我的特定代码中,它是在最后一个 render() 之前调用的。
示例代码:
class ComponentB extends Component {
constructor(props) {
super(props) // added upon first reply
this.handleObjectAdd = this.handleObject.bind(this);
this.state.objects = [];
}
handleObjectAdd() {
this.state.objects.unshift({prop1: this.state.objects.length + 1});
}
render() {
return (
<div>
<button onClick={this.handleObjectAdd}>ADD</button>
{ this.state.objects.map((object, index) =>
<ComponentA key={index} details={object}/>
)
}
</div>
)
})
}
}
class ComponentA extends Component {
constructor(props) {
super(props) // added upon first reply
console.log('ComponentA constructor called');
}
render() {
console.log('ComponentA render() called');
return (
<input type="text" value={this.props.details.prop1}></input>
)
}
}
对于 5 个元素的数组(所有 ComponentA 实例),我得到以下行:
ComponentA render() called
ComponentA render() called
ComponentA render() called
ComponentA render() called
ComponentA constructor called
ComponentA render() called
此外,构造函数的日志行始终出现在最后一个构造函数日志行之前,无论数组元素的数量如何。
为什么日志输出如上?将不胜感激。
这被标记为 React Rerender dynamic Components (Solr) 的副本,但它不是。
我理解你的问题 "why when I put new element to the very start of array it seems new React component is created for the very last element?"
原因是 key
与 index
一起使用。
基于其逻辑(检查文档中的 Lists and Keys 部分)因为只有最后一个元素(新索引为 oldLength + 1
)具有唯一性 key
只有它会从头开始创建。而所有其他人只是重新渲染和更新。换句话说,您的代码更新 N - 1
元素,而不是仅仅创建 1 个新元素并保持所有其他元素不变。
要解决这个问题,您不应依赖 key
中的 index
,而应使用其他一些可预测的、稳定的和独特的值。在你的例子中是 prop1
。然后将为第一个元素调用构造函数。
这是更新版本
class ComponentB extends Component {
constructor(props) {
super(props) // added upon first reply
this.state = {
objects: []
};
}
handleObjectAdd = () => {
this.setState(oldState => ({
objects: [
{prop1: this.state.objects.length + 1},
...oldState.objects
]
}))
}
render() {
return (
<div>
<button onClick={this.handleObjectAdd}>ADD</button>
{this.state.objects.map(obj =>
<ComponentA key={obj.prop1} details={obj} />
)}
</div>
)
}
}
class ComponentA extends Component {
constructor(props) {
super(props) // added upon first reply
console.log('ComponentA constructor called');
}
render() {
console.log('ComponentA render() called');
return (
<input type="text" value={this.props.details.prop1}></input>
)
}
}
您的初始代码几乎没有语法错误,并且直接改变了状态(永远不要那样做),因此它在单击按钮后永远不会重新呈现。下次 post 提问时请检查代码 - 这会让 understanding/answering 更容易。
[UPD] 从下面的评论中提取了一些片段
instead of just creating 1 new and leaving all other unchanged.
React 不会尝试最小化操作数。在比较 [1,2,3] 和 [0, 1, 2, 3] 时,有两种可能的方式:'insert 0 at the beginning, shifting everything else' 或 'decrement all the elements by 1 and additionally insert 3 at the end'。如果您提供良好的 属性 作为关键,React 会选择第一个解决方案。但是如果有 key={index} 你实际上是在说“反应,使用第二种方法,我知道我在做什么”。在调用 .setState 之前,React 不会分析您 运行 的代码,它只依赖键值。
我有一个 React 组件,它在另一个组件中使用 Array.prototype.map() 多次渲染。
第一个组件的构造函数应该被调用多少次?我期待与映射数组的长度一样多的次数,但它似乎只被调用了一次。
在我的特定代码中,它是在最后一个 render() 之前调用的。
示例代码:
class ComponentB extends Component {
constructor(props) {
super(props) // added upon first reply
this.handleObjectAdd = this.handleObject.bind(this);
this.state.objects = [];
}
handleObjectAdd() {
this.state.objects.unshift({prop1: this.state.objects.length + 1});
}
render() {
return (
<div>
<button onClick={this.handleObjectAdd}>ADD</button>
{ this.state.objects.map((object, index) =>
<ComponentA key={index} details={object}/>
)
}
</div>
)
})
}
}
class ComponentA extends Component {
constructor(props) {
super(props) // added upon first reply
console.log('ComponentA constructor called');
}
render() {
console.log('ComponentA render() called');
return (
<input type="text" value={this.props.details.prop1}></input>
)
}
}
对于 5 个元素的数组(所有 ComponentA 实例),我得到以下行:
ComponentA render() called
ComponentA render() called
ComponentA render() called
ComponentA render() called
ComponentA constructor called
ComponentA render() called
此外,构造函数的日志行始终出现在最后一个构造函数日志行之前,无论数组元素的数量如何。
为什么日志输出如上?将不胜感激。
这被标记为 React Rerender dynamic Components (Solr) 的副本,但它不是。
我理解你的问题 "why when I put new element to the very start of array it seems new React component is created for the very last element?"
原因是 key
与 index
一起使用。
基于其逻辑(检查文档中的 Lists and Keys 部分)因为只有最后一个元素(新索引为 oldLength + 1
)具有唯一性 key
只有它会从头开始创建。而所有其他人只是重新渲染和更新。换句话说,您的代码更新 N - 1
元素,而不是仅仅创建 1 个新元素并保持所有其他元素不变。
要解决这个问题,您不应依赖 key
中的 index
,而应使用其他一些可预测的、稳定的和独特的值。在你的例子中是 prop1
。然后将为第一个元素调用构造函数。
这是更新版本
class ComponentB extends Component {
constructor(props) {
super(props) // added upon first reply
this.state = {
objects: []
};
}
handleObjectAdd = () => {
this.setState(oldState => ({
objects: [
{prop1: this.state.objects.length + 1},
...oldState.objects
]
}))
}
render() {
return (
<div>
<button onClick={this.handleObjectAdd}>ADD</button>
{this.state.objects.map(obj =>
<ComponentA key={obj.prop1} details={obj} />
)}
</div>
)
}
}
class ComponentA extends Component {
constructor(props) {
super(props) // added upon first reply
console.log('ComponentA constructor called');
}
render() {
console.log('ComponentA render() called');
return (
<input type="text" value={this.props.details.prop1}></input>
)
}
}
您的初始代码几乎没有语法错误,并且直接改变了状态(永远不要那样做),因此它在单击按钮后永远不会重新呈现。下次 post 提问时请检查代码 - 这会让 understanding/answering 更容易。
[UPD] 从下面的评论中提取了一些片段
instead of just creating 1 new and leaving all other unchanged.
React 不会尝试最小化操作数。在比较 [1,2,3] 和 [0, 1, 2, 3] 时,有两种可能的方式:'insert 0 at the beginning, shifting everything else' 或 'decrement all the elements by 1 and additionally insert 3 at the end'。如果您提供良好的 属性 作为关键,React 会选择第一个解决方案。但是如果有 key={index} 你实际上是在说“反应,使用第二种方法,我知道我在做什么”。在调用 .setState 之前,React 不会分析您 运行 的代码,它只依赖键值。