React setState 更新不正确的子组件



我正在构建一个博客文章系统,用户可以在其中创建多个帖子,所有文章都显示在一个页面上,如果需要,可以使用TinyMCE编辑器直接在同一页面上编辑它们。

每篇博客文章都是它自己的 React 组件,具体称为 Post。帖子组件类负责呈现帖子(即标题、正文、作者等),这包括编辑帖子表单。它从应用程序组件传递的道具中获取帖子数据,例如标题和正文。

App 组件是入口点,它从我的服务器检索所有 JSON 格式的博客文章,为每个帖子创建一个 Post 组件,传递相应的 props 并将整个 Post 组件推送到一个数组中。完成此操作后,它将调用 this.setState() 并更新 posts 数组。

但是,如果创建了多个帖子,并且我调用 this.handleEdit() 和单个 Post 组件,它将更新错误的 Post 组件的状态。

App.tsx

class App extends React.Component<IProps, IState> {
constructor(props: Readonly<IProps>) {
super(props);
this.state = {
posts: []
}
}
componentWillMount = () => {
var req = new HTTPRequest("GET", "/qa/posts")
req.execVoid(HTTP.RESPONSE.OK).then(function (data: []) {
var posts = [];
data.forEach(function (entry, index) {
posts.push(
<Post
id={entry['id']}
title={entry['title']}
body={entry['body']}
author={entry['author']}
date={entry['created_at']}
showDate={entry['showDate']}
deletePost={() => this.deleteComponent(index)}
key={index}
/>
)
}.bind(this))

this.updatePosts(posts)
}.bind(this))
}
updatePosts = (posts: Array<any>) => {
if (posts.length == 0) {
posts.push(
<div className="card" key={1}>
<div className="card-content">
No posts to show :)
</div>
</div>
)
}
this.setState({
posts: posts
})

}
deleteComponent = (key: number) => {
let posts = this.state.posts.filter(function (value, index) {
return index != key;
})
this.updatePosts(posts);
}
componentDidMount = () => {

}

render(): React.ReactNode {
return (
<div>
{this.state.posts}
</div>
)
}
}
export default App;

当我单击this.actions()方法中显示的"取消"按钮时,this.state.editEnabled设置为 true,它不会更新当前 Post 类的状态,而是似乎更新了创建 posts 数组应用程序中的另一个帖子。特别是,"取消"按钮将调用this.disableEdit(),将this.state.editEnabled更新为 false。但是,它不会为当前帖子执行此操作,而是为数组中的另一个帖子执行此操作,似乎是随机的......尝试打印出与帖子关联的帖子标题也会给出不正确的帖子标题,如this.disableEdit()

邮政.tsx

class Post extends React.Component<IProps, IState> {

constructor(props: Readonly<IProps>) {
super(props);
this.state = {
id: -1,
title: "",
body: "",
author: "",
date: "",
showDate: true,
editEnabled: false,
showProgressBar: false,
edit: {
title: "",
}
};
}
componentDidMount = () => {
this.setState({
id: this.props['id'],
title: this.props['title'],
body: this.props['body'],
author: "",//this.props['author'],
date: this.convertToReadableDate(this.props['date']),
showDate: !!this.props['showDate'],
})
tinymce.init({
selector: "#edit_body_" + this.props['id'],
skin_url: '/lib/tinymce/skins/ui/oxide',
})

}
convertToReadableDate(unix_timestamp: number): string {
var date = new Date(unix_timestamp * 1000);
return date.toISOString().split("T")[0];
}
handleDelete = () => {
if (confirm("Are you sure you would like to delete this post?")) {
var req = new HTTPRequest("DELETE", "/qa/posts/" + this.state.id);
req.execVoid(HTTP.RESPONSE.OK)
.then(function () {
this.props.deletePost();
M.toast({ html: "Your post was deleted!", classes: "green" })
}.bind(this))
.catch(function (err: Error) {
M.toast({
html: "We have trouble deleting your post. Try again later",
classes: "red"
});
console.error(err.message);
}.bind(this))
}
}
promptSaveChange = () => {
if (this.state.title != this.state.edit.title || tinymce.get('edit_body_' + this.props.id).getContent() !== this.state.body) {
return confirm("You have unsaved changes. Are you sure you would like to proceed?")
} else {
return true;
}
}
handleEdit = () => {
if (this.state.editEnabled) {
if (this.promptSaveChange()) {
this.disableEdit();
}
} else {
this.enableEdit();
tinymce.get('edit_body_' + this.props.id).setContent(this.state.body);
}
}
resetChanges = () => {
this.setState({
edit: {
title: this.state.title
}
})
tinymce.get('edit_body_' + this.props.id).setContent(this.state.body);
}
handleEditSave = () => {
this.showProgress();
var req = new HTTPRequest("PATCH", "/qa/posts/" + this.state.id);
var body_content = tinymce.get('edit_body_' + this.props.id).getContent();
req.execAsJSON({
title: this.state.edit.title,
body: body_content
}, HTTP.RESPONSE.ACCEPTED).then(function (ret) {
this.setState({
title: this.state.edit.title,
body: body_content
});
this.disableEdit();
M.toast({
html: ret['msg'],
classes: 'green'
})
}.bind(this)).catch(function (err: Error) {
console.log(err.message);
M.toast({
html: "We had trouble updating the post. Try again later."
})
}.bind(this)).finally(function () {
this.hideProgress();
})
}
handleTitleEdit = (e) => {
this.setState({
edit: {
title: e.target.value
}
})
}
enableEdit = () => {
this.setState({
editEnabled: true,
edit: {
title: this.state.title
}
}, function () {
M.AutoInit();
})
}
disableEdit = () => {
console.log('disabled: ' + this.state.title);
this.setState({
editEnabled: false
})
}
showProgress = () => {
this.setState({
showProgressBar: true
})
}
hideProgress = () => {
this.setState({
showProgressBar: false
})
}

content = () => {
return (
<div>
<div style={{ display: this.state.editEnabled ? 'none' : null }}>
<span className="card-title">{this.state.title}</span>
<div dangerouslySetInnerHTML={{ __html: this.state.body }}></div>
<small> {this.state.showDate ? "Posted at: " + this.state.date : ""}</small>
</div>
<div style={{ display: this.state.editEnabled ? null : 'none' }}>
<input type="text" name="title" value={this.state.edit.title} placeholder={this.state.title} onChange={this.handleTitleEdit} />
<textarea id={"edit_body_" + this.props.id}></textarea>
</div>
</div>
)
}
actions = () => {
return (
<>
<div className="row" style={{ display: this.state.editEnabled ? null : 'none' }}>
<a className="btn-small green waves-effect" onClick={this.handleEditSave}><i className="material-icons left">save</i> Save</a>
<a className='dropdown-trigger btn-flat blue-text' href='#' data-target='edit-options'>More</a>
<ul id='edit-options' className='dropdown-content'>
<li>
<a href="#!" className="orange-text" onClick={this.resetChanges}>Reset Changes</a>
</li>
<li>
<a href="#!" className="orange-text" onClick={this.handleEdit}>Cancel</a>
</li>
<li>
<a href="#!" className="red-text" onClick={this.handleDelete}>Delete</a>
</li>
</ul>

<div className="progress" style={{ display: this.state.showProgressBar ? null : "none" }}>
<div className="indeterminate"></div>
</div>
</div>
<div className="row" style={{ display: this.state.editEnabled ? 'none' : null }}>
<a className="btn-small orange waves-effect" onClick={this.handleEdit}><i className="material-icons left">edit</i> Edit</a>
</div>
</>
)
}
render(): React.ReactNode {
return (
<div className="card">
<div className="card-content">
{this.content()}
</div>
<div className="card-action">
{this.actions()}
</div>
</div>
)
}
}
export default Post;   

好的。我已经解决了这个有趣的问题。事实证明,React 不是问题所在。我使用的Materialize CSS框架造成了这个问题,特别是M.AutoInit()

在错误的位置调用它可能会导致 React 的事件处理程序出现问题。

最新更新