状态更改后反应无法正确渲染
Posted
技术标签:
【中文标题】状态更改后反应无法正确渲染【英文标题】:React not rendering correctly after state change 【发布时间】:2022-01-18 00:33:23 【问题描述】:我有一个简单的表格。您单击“添加项目”按钮并出现一个文本框。模糊时,在文本框中输入的文本被添加到状态变量数组中。再次单击“添加项目”按钮,出现另一个文本框,依此类推。
对于每个文本框,还有一个“删除项目”按钮。单击此按钮时,当前项从数组中移除,当前文本框从页面中移除。
class App extends React.Component
constructor(props)
super(props);
this.state =
items: []
addItem()
this.setState(
items: [...this.state.items, []]
)
removeItem(index)
//var items = this.state.items;
var items = [...this.state.items];
items.splice(index, 1);
this.setState(
items: items
)
changeItem(e, index)
var items = this.state.items;
items[index] = e.target.value;
this.setState(
items: items
)
render()
return (
<div>
this.state.items.map((item, index) =>
return (
<React.Fragment key=index>
<hr />
<Row>
<Col column sm="8">
<Form.Control
type="text"
name="item"
onBlur=(e) => this.changeItem(e, index)
/>
</Col>
</Row>
<Row>
<Col column sm="8">
<Button
onClick=() => this.removeItem(index)
variant="link"
size="sm">
Remove Item
</Button>
</Col>
</Row>
</React.Fragment>
)
)
<br />
<Button
onClick=(e) => this.addItem(e)
variant="outline-info">Add item
</Button>
</div>
)
我遇到的问题是,虽然数组在removeItem(index)
中修改成功,但从页面中删除的文本框始终是最后添加的,而不是应该删除的。例如:
-
点击“添加项目”,输入:aaa
items: ['aaa']
点击“添加项目”,输入:bbb items: ['aaa', 'bbb']
点击“添加项目”,输入:ccc items: ['aaa', 'bbb', 'ccc']
点击aaa下的“删除项目”。项目成功更新:items: ['bbb', 'ccc']
页面应该显示一个带有 bbb 的文本框和一个带有 ccc 的文本框。但它显示:
如何从页面中删除正确的文本框?
【问题讨论】:
你不应该在removeItem
函数中使用splice
,因为splice
改变了它正在处理的数组,所以基本上你在不使用setState函数的情况下改变了状态,这是不正确的在反应。相反,您应该使用[...this.state.items]
创建一个具有相同内容的新数组,然后在该新数组上使用splice
,然后使用该新数组设置状态。
` items: [...this.state.items, []]` 添加的项目看起来不正确。 ` items: [...this.state.items, '']` .其他一切看起来都很好
尝试在文本框中添加一个键
@RishabhGupta - 我确实在 removeItem() 中创建了一个新数组。我没有直接修改状态。如果你打算在 addItem() 中这样做,我试过了,但我仍然遇到同样的问题。
@RishabhGupta - 我更新了代码,但我仍然遇到同样的问题。
【参考方案1】:
您的代码存在一些问题:
-
首先,您直接更改
this.state
,而不使用changeItem
函数中的this.setState()
,我已将其更改为var items = [...this.state.items];
您正在将index
用作列表项的key
,并使用<React.Fragment key=index>
中的this.state.items.map((item, index) => ...)
进行渲染。这个key
应该是列表项的唯一标识符,通常是数据库中唯一的id
。 Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity
。但是,在您的情况下,由于您没有项目的唯一 ID,因此我正在使用 uuid
模块创建这些项目。了解更多关于按键的信息:https://reactjs.org/docs/lists-and-keys.html 和 https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318
我已经从Col
组件中删除了column
属性,因为它给出了一些警告
因为,现在我要为每个列表项添加一个唯一的 id,我已将 this.state.items
的结构从 array of strings
更改为 array of objects, where each object has a id and data, where data is text
您在使用 Form.Control
组件时没有使用 value 属性。假设您添加了一个列表项,在输入中写了一些内容,再次单击add item
按钮。此时,由于您更改了焦点,onBlur
事件将触发,您的this.state.items
将相应更改,到目前为止,非常好。但是现在当它再次重新渲染整个东西时,它将重新渲染 Form.Control
组件,但是没有 value
道具,这个组件将不知道要显示什么数据,因此它将呈现为空字段.因此,我在这个组件中添加了 value
属性
由于我在Form.Control
组件中添加了value
属性,因此现在反应要求我将onChange
事件添加到组件中,否则它将呈现为只读输入,因此我将onBlur
更改为onChange
事件。当onChange
已经存在时,onBlur
无需更改状态值。
这是完成的代码:
import React from "react";
import v4 from 'uuid';
import Button, Row, Col, Form from "react-bootstrap";
class App extends React.Component
constructor(props)
super(props);
this.state =
items: []
;
addItem()
this.setState(
items: [...this.state.items, data: "", id: v4()]
);
removeItem(index)
console.log("dbg1", index);
//var items = this.state.items;
var items = [...this.state.items];
items.splice(index, 1);
this.setState(
items: items
);
changeItem(e, index)
console.log("dbg2", this.state.items);
var items = [...this.state.items];
items[index].data = e.target.value;
this.setState(
items: items
);
render()
console.log("dbg3", this.state.items);
return (
<div>
this.state.items.map((item, index) =>
return (
<React.Fragment key=item.id>
<hr />
<Row>
<Col sm="8">
<Form.Control
type="text"
name="item"
value=item.data
onChange=(e) => this.changeItem(e, index)
// onBlur=(e) => this.changeItem(e, index)
/>
</Col>
</Row>
<Row>
<Col sm="8">
<Button
onClick=() => this.removeItem(index)
variant="link"
size="sm"
>
Remove Item
</Button>
</Col>
</Row>
</React.Fragment>
);
)
<br />
<Button onClick=(e) => this.addItem(e) variant="outline-info">
Add item
</Button>
</div>
);
export default App;
【讨论】:
我相信函数也需要绑定。以上是关于状态更改后反应无法正确渲染的主要内容,如果未能解决你的问题,请参考以下文章