javascript - componentDidUpdate 中的 setState 将 prev 与当前状态进行比较,无法用新数据重新渲染页面,也会出现无限循环
问题描述
我是 React 的初学者,在这种特殊情况下,我可能遗漏了一些非常基本的东西。
在这里,我有一个简单的 CRUD 应用程序,在用户添加新数据后,应该呈现更新的项目列表。新数据通过第二个 Dialog 组件AddNewDevelopmentWork.js 添加
因此,在AddNewDevelopmentWork.js(它是只会打开一个对话框供用户输入和填写几个 TestFields 的子组件)添加新数据后,在主组件(DevelopmentList.js)中,我使用componentDidUpdate来与当前状态和 prevState 进行比较(对于状态变量allDevelopmentWorks,它是一个对象数组),如果它们不相等,则向后端 Express API 发出请求,并在componentDidUpdate中获取数据和更新状态。然后用新数据渲染。
问题是,这个主要的DevelopmentList.js组件在页面刷新之前不会呈现用户输入的新数据。但是在手动刷新页面后,它会显示新输入的数据。
这是我的DevelopmentList组件。
class DevelopmentList extends Component {
constructor(props) {
super(props);
this.state = {
allDevelopmentWorks: []
};
}
componentDidUpdate(prevProps, prevState) {
if (
this.state.allDevelopmentWorks.length !==
prevState.allDevelopmentWorks.length
) {
return axios
.get("/api/developmenties")
.then(res => {
this.setState({
allDevelopmentWorks: res.data
});
})
.catch(function(error) {
console.log(error);
});
}
}
componentDidMount() {
axios.get("/api/developmenties").then(res => {
this.setState({
allDevelopmentWorks: res.data
});
});
}
render() {
const { classes } = this.props;
return (
<div>
<Table className={classes.table}>
<TableHead>
<TableRow className={classes.row}>
<CustomTableCell align="left">Location</CustomTableCell>
<CustomTableCell align="left">
Description Of Work
</CustomTableCell>
<CustomTableCell align="left">
Date of Commencement
</CustomTableCell>
<CustomTableCell align="left">Date of Completion</CustomTableCell>
<CustomTableCell align="left">Status of Work</CustomTableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.allDevelopmentWorks.map((document, i) => (
<TableRow className={classes.row} key={i}>
<CustomTableCell component="th" scope="row">
{document.location}
</CustomTableCell>
<CustomTableCell align="left">
{document.work_description}
</CustomTableCell>
<CustomTableCell align="left">
{moment(document.date_of_commencement).format("YYYY-MM-DD")}
</CustomTableCell>
<CustomTableCell align="left">
{moment(document.date_of_completion).format("YYYY-MM-DD")}
</CustomTableCell>
<CustomTableCell align="left">
{document.status_of_work}
</CustomTableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
}
export default withStyles(styles)(DevelopmentList);
但是,使用下面的componentDidUpdate方法,如果我将其更改为如下 if 条件(将长度属性排除在等式之外),则新数据会立即呈现在页面上,但随后它也会在componentDidUpdate内部变成无限循环并命中每秒一次又一次地使用 Express API。
componentDidUpdate(prevProps, prevState) {
if (
this.state.allDevelopmentWorks !==
prevState.allDevelopmentWorks
) {
return axios
.get("/api/developmenties")
.then(res => {
this.setState({
allDevelopmentWorks: res.data
});
})
.catch(function(error) {
console.log(error);
});
}
}
第二个组件中的代码(它是主 DevelopmentList.js组件的子组件,只会打开一个对话框供用户输入并填充几个 TestFields 将新数据添加到此 CRUD)在AddNewDevelopmentWork.js下面
class AddNewDevelopmentWork extends Component {
state = {
open: false,
location: "",
work_description: "",
date_of_commencement: new Date(),
date_of_completion: new Date(),
status_of_work: "",
vertical: "top",
horizontal: "center"
};
handleCommencementDateChange = date => {
this.setState({
date_of_commencement: date
});
};
handleCompletionDateChange = date => {
this.setState({
date_of_completion: date
});
};
handleToggle = () => {
this.setState({
open: !this.state.open
});
};
handleClickOpen = () => {
this.setState({ open: true });
};
handleClose = () => {
this.props.history.push("/dashboard/developmentworks");
};
onChange = e => {
const state = this.state;
state[e.target.name] = e.target.value;
this.setState(state);
};
handleFormSubmit = e => {
e.preventDefault();
const {
location,
work_description,
date_of_commencement,
date_of_completion,
status_of_work
} = this.state;
axios
.post("/api/developmenties/", {
location,
work_description,
date_of_commencement,
date_of_completion,
status_of_work
})
.then(() => {
// this.props.history.push("/dashboard/developmentworks");
// window.location.href = window.location.href;
this.setState({
open: false,
vertical: "top",
horizontal: "center"
});
})
.catch(error => {
alert("Ooops something wrong happened, please try again");
});
};
handleCancel = () => {
this.setState({ open: false });
};
render() {
const { classes } = this.props;
const {
location,
work_description,
date_of_commencement,
date_of_completion,
status_of_work,
vertical,
horizontal
} = this.state;
return (
<MuiThemeProvider theme={theme}>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<div>
<MuiThemeProvider theme={theme}>
<Dialog open={this.state.open} onClose={this.handleToggle}>
<DialogContent required>
<form onSubmit={this.handleFormSubmit}>
<TextField
value={location}
onChange={e =>
this.setState({
location: e.target.value
})
}
error={location === ""}
helperText={
location === "" ? "Please enter Location" : " "
}
label="Location"
type="email"
fullWidth
/>
<TextField
value={work_description}
onChange={e =>
this.setState({
work_description: e.target.value
})
}
error={work_description === ""}
helperText={
work_description === ""
? "Please enter Work Description"
: " "
}
label="Description of Work"
type="email"
fullWidth
/>
<div>
<DatePicker
format="dd/MM/yyyy"
label="Date of Commencement"
value={date_of_commencement}
onChange={this.handleCommencementDateChange}
disableOpenOnEnter
animateYearScrolling={false}
/>
</div>
<div>
<DatePicker
format="dd/MM/yyyy"
label="Date of Completion"
value={date_of_completion}
onChange={this.handleCompletionDateChange}
/>
</div>
<TextField
value={status_of_work}
onChange={e =>
this.setState({
status_of_work: e.target.value
})
}
error={location === ""}
helperText={
status_of_work === ""
? "Please enter Status of Work!"
: " "
}
label="Status of Work"
type="email"
fullWidth
/>
</form>
</DialogContent>
<DialogActions>
<Button
onClick={this.handleCancel}
classes={{
root: classes.root
}}
variant="contained"
>
Cancel
</Button>
<Button
onClick={this.handleFormSubmit}
color="primary"
variant="contained"
>
Save
</Button>
</DialogActions>
</Dialog>
</MuiThemeProvider>
</div>
</MuiPickersUtilsProvider>
</MuiThemeProvider>
);
}
}
解决方案
问题是您没有将状态视为不可变的(如 React 文档所建议的那样)。当您调用 时this.setState({ allDevelopmentWorks: res.data })
,您将allDevelopmentWorks
用一个新的数组的值替换一个新的对象引用。因为数组引用不匹配,直接检查相等会失败(即this.state.allDevelopmentWorks !== prevState.allDevelopmentWorks
比较对象引用)。
查看此答案以更新状态数组而不改变它。
并查看loadashisEqual
以比较数组相等性。
推荐阅读
- windows - 在 Windows 上,QWidget::setCursor 仅在鼠标移动 1 个像素时生效
- sql - 如何在 SQL 中插入时连接 N' 字符串
- python - 使用 matplotlib 设置 yticks 标签
- ios - 世博会裸工作流谷歌登录身份验证错误
- javascript - 使用各自的按钮隐藏 HTML 块
- html - 导航栏下的页面内容使用 CSS 动画移动
- html - EmojiOneArea 覆盖输入的 html 属性
- javascript - 如何在地图中搜索部分样式?
- java - 不支持从 UNKNOWN 到 UNKNOWN 的转换 - SpringBatch
- javascript - 同时发出多个 API 请求