尝试更新待办事项应用程序中的列表时无法调用 setState()
Cannot call setState() when attempting to update list in todo app
这似乎是一个常见问题,但我已经尝试了多种配置,并且始终收到错误消息 Warning: Can't call setState on a component that is not yet mounted
。我在我的应用程序的其他组件中以类似的方式使用 setState()
没有问题,所以我不确定为什么我的 App
组件认为我正在尝试在构造函数中调用 setState()
很明显它在外面。
这是我尝试使用componentDidMount()
生命周期方法。显然我完全遗漏了一些东西,但我无法阅读 setState()
的文档。 特别是 考虑到我在我的应用程序的其他部分使用了这种完全相同的语法,没有任何问题。无论如何,这就是我正在使用的东西:
import react from 'react';
class App extends react.Component {
constructor() {
super();
this.state = {
tasks: [
{
content:"walk dog",
date:"7/17/21",
priority:"high"
},
{
content:"take out trash",
date:"7/17/21",
priority:"low"
},
],
};
}
componentDidMount() {
this.addTask = this.addTask.bind(this);
}
addTask(content, date, priority) {
let taskUpdate = this.state.tasks;
let task = {
content: content,
date: date,
priority: priority
};
taskUpdate.push(task);
this.setState({
tasks: taskUpdate
});
}
render() {
return (
<>
<Header />
<Sidebar taskList={this.state.tasks} groupList={this.state.groups}>
</Sidebar>
</>
)
}
}
export { App };
要点是更新我的 App
组件的状态,以便我可以作为 props 传递并重新呈现 Sidebar
组件中的任务列表。提前道歉,因为这似乎已被问过一百万次,但我只是感到困惑。
编辑
为澄清起见,addTask
方法是在作为“将此任务添加到任务列表”模式的一部分呈现的表单中调用的,例如:
const AddModal = ({ closeBtn, show, children }) => {
class Form extends Component {
constructor(props) {
super(props);
this.state = {
content: '',
date: '',
highPriority: ''
};
this.handleContent = this.handleContent.bind(this);
this.handleDate = this.handleDate.bind(this);
this.handlePriority = this.handlePriority.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleContent(event) {
this.setState({content: event.target.value});
}
handleDate(event) {
this.setState({date: event.target.value});
}
handlePriority(event) {
this.setState({highPriority: event.target.checked});
}
handleSubmit(event) {
event.preventDefault();
console.log("content: " + this.state.content + "\n" +
"date: " + this.state.date + "\n" +
"high priority: " + this.state.highPriority);
new App().addTask(this.state.content, this.state.date, this.state.highPriority);
closeBtn();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.content}
onChange={this.handleContent}
placeholder="add a task (max 30 chars)"
maxLength="30"></input>
<input
type="date"
onChange={this.handleDate}></input>
<div className="checkbox-container">
<input
type="checkbox"
name="priority-checkbox"
onChange={this.handlePriority}></input>
<label
htmlFor="priority-checkbox">
high priority?
</label>
<button
type="submit"
id="add-task-submit">
+
</button>
</div>
</form>
);
}
};
const modalClassName = show ? "add-task-modal display-block" : "add-task-modal display-none";
return (
<div className={modalClassName}>
<div className="add-task-modal-content">
{children}
<button type="button" id="close-btn" onClick={closeBtn}>
x
</button>
<Form></Form>
</div>
</div>
);
};
包含此表单的模态呈现在一个仅显示任务列表的文件中。 addTask()
方法成功地将表单数据传递给 'App.js',因为我可以直接更改状态,例如this.state.tasks = [...]
,但据我所知,这绝对不是最佳实践,我需要改用 setState()
。
编辑 2
因此,当我在改变状态的一个简单问题上跌跌撞撞时,开始变成了对 React 作为框架的简洁而有用的概述。非常感谢用户 Robin Zigmond 的撰写,并感谢 Alexander Staroselsky 进一步阐述了使用 redux 库的建议。您的意见帮助很大,我的应用程序的组件现在可以正确地相互通信。
只要说我最初的错误并不是 setState
的真正问题就够了,这有助于我在更广泛的层面上理解 React。再次感谢所有贡献的人。
你不应该就地改变数组,当你使用 push
对数组的引用不会改变。但不确定这是否与您的警告有关。
addTask(content, date, priority) {
this.setState({
tasks: this.state.tasks.concat({
content,
date,
priority
})
});
}
为什么用 new
调用 addTask
?你不应该这样做。
new App().addTask(this.state.content, this.state.date, this.state.highPriority);
可以使用context或redux
传递值和更新函数到模态。
您可以将 addTask 函数替换为:
addTask(content, date, priority) {
let task = {
content: content,
date: date,
priority: priority
};
this.setState({
tasks: [...this.state.tasks, task]
});
}
问题可以在您的 AddModal
组件中看到:
new App().addTask(this.state.content, this.state.date, this.state.highPriority);
手动为您的组件之一创建一个新实例 - 正如您在此处使用 new App()
所做的那样 - 不是您在 React 中永远需要做的事情。框架本身处理所有组件的生命周期——包括构建它们。
在这种情况下,警告本身就暴露了:“无法在尚未安装的组件上调用 setState”。 “mounted”是 React 中的一个概念,它仅指组件实例是否已呈现为用户界面的一部分——换句话说,它是否在页面上。 React 是一个 library/framework,其唯一目的是构建用户界面 - 每个组件都应该是您可以将 non-technical 用户指向 page/app 的一部分并说“那是此组件控制的部分”。在页面上拥有一个实际未呈现(或“挂载”以使用 React 术语)的组件实例没有任何意义。
从技术上讲,在准备好“挂载”组件之前,React 甚至不会调用您的 class 的构造函数 - 因此在调用构造函数之后,React 将调用您的组件的 render
方法然后(如果它有一个)它的 componentDidMount
方法。 (请参阅 React 组件生命周期图 here。)当您在 render
内的 JSX 中包含组件时,React 会自行处理所有这些 - 因此您不必担心。但是通过手动实例化 new App()
你是在构建一个组件实例而不安装它,React 不是设计来处理的,因此它有理由抱怨。
您似乎试图做的事情在更深层次上没有意义 - 因为您正在尝试更新组件状态,但组件不应该 状态尚未安装。同样,状态应该对应于 UI 中的某些内容,即使是在稍微抽象的层面上——它可能是用户当前在输入字段中输入的内容,或者更抽象的东西,比如标志某些动作已经完成或未完成,或者某个特定动作已经完成的次数 - 或者您想要的任何其他内容。但它对应于 something,并且该 something 与页面上组件内部发生的事情密切相关。如果它不在页面上 - 那么您的状态实际上是什么?
到 return 你实际应该做什么:我假设你的 App
是你应用程序的根组件,并且 AddModal
出现在它里面的某个地方。如果它是一个直接的 child,这将很简单,因为你的 App
总是在 AddModal
出现时呈现,你可以简单地将 addTask
方法作为 prop 传递下去。换句话说,在 App
中 render
内的某处,您将拥有:
<AddModal addTask={ this.addTask } />
然后在 AddModal
中称其为:
this.props.addTask(/*whatever arguments you need*/);
这会将 parent 组件的状态更新为您需要的状态。 (这里似乎正在更新 Sidebar
组件内的数据。)
如果看起来 AddModal
实际上下降了几级,则您必须将 addTask
下降几级。 IE。在 App
的 render
内,您将拥有
<SomeChildComponent addTask={ this.addTask } />
然后在SomeChildComponent
里面,说
<SomeOtherChild addTask={ this.props.addTask } />
依此类推,直到您最终达到
<AddModal addTask={ this.props.addTask } />
如果你有超过 1 或 2 个级别并且不需要中间级别的 addTask
,这肯定会变得乏味和脆弱 - 所以你可能想看看其他解决方案,如上下文, 或 Redux。但这就是你在“基本 React”中做事的方式,我认为你最好先理解这一点,然后再仔细研究其他模式。 (我坚信在开始使用 Redux 之类的库之前需要了解它们正在解决什么问题,因为“其他人都这样做”。)
我还建议阅读 this 以了解如何处理一个组件需要更新另一个不是直接 child 或 [=77 的状态的情况=] - 这似乎是你想要做的。
抱歉,我一直在胡说八道,但我希望这能让您更好地了解 React 的工作原理以及如何处理状态,以及为什么您不应该手动构建自己的组件。
这似乎是一个常见问题,但我已经尝试了多种配置,并且始终收到错误消息 Warning: Can't call setState on a component that is not yet mounted
。我在我的应用程序的其他组件中以类似的方式使用 setState()
没有问题,所以我不确定为什么我的 App
组件认为我正在尝试在构造函数中调用 setState()
很明显它在外面。
这是我尝试使用componentDidMount()
生命周期方法。显然我完全遗漏了一些东西,但我无法阅读 setState()
的文档。 特别是 考虑到我在我的应用程序的其他部分使用了这种完全相同的语法,没有任何问题。无论如何,这就是我正在使用的东西:
import react from 'react';
class App extends react.Component {
constructor() {
super();
this.state = {
tasks: [
{
content:"walk dog",
date:"7/17/21",
priority:"high"
},
{
content:"take out trash",
date:"7/17/21",
priority:"low"
},
],
};
}
componentDidMount() {
this.addTask = this.addTask.bind(this);
}
addTask(content, date, priority) {
let taskUpdate = this.state.tasks;
let task = {
content: content,
date: date,
priority: priority
};
taskUpdate.push(task);
this.setState({
tasks: taskUpdate
});
}
render() {
return (
<>
<Header />
<Sidebar taskList={this.state.tasks} groupList={this.state.groups}>
</Sidebar>
</>
)
}
}
export { App };
要点是更新我的 App
组件的状态,以便我可以作为 props 传递并重新呈现 Sidebar
组件中的任务列表。提前道歉,因为这似乎已被问过一百万次,但我只是感到困惑。
编辑
为澄清起见,addTask
方法是在作为“将此任务添加到任务列表”模式的一部分呈现的表单中调用的,例如:
const AddModal = ({ closeBtn, show, children }) => {
class Form extends Component {
constructor(props) {
super(props);
this.state = {
content: '',
date: '',
highPriority: ''
};
this.handleContent = this.handleContent.bind(this);
this.handleDate = this.handleDate.bind(this);
this.handlePriority = this.handlePriority.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleContent(event) {
this.setState({content: event.target.value});
}
handleDate(event) {
this.setState({date: event.target.value});
}
handlePriority(event) {
this.setState({highPriority: event.target.checked});
}
handleSubmit(event) {
event.preventDefault();
console.log("content: " + this.state.content + "\n" +
"date: " + this.state.date + "\n" +
"high priority: " + this.state.highPriority);
new App().addTask(this.state.content, this.state.date, this.state.highPriority);
closeBtn();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.content}
onChange={this.handleContent}
placeholder="add a task (max 30 chars)"
maxLength="30"></input>
<input
type="date"
onChange={this.handleDate}></input>
<div className="checkbox-container">
<input
type="checkbox"
name="priority-checkbox"
onChange={this.handlePriority}></input>
<label
htmlFor="priority-checkbox">
high priority?
</label>
<button
type="submit"
id="add-task-submit">
+
</button>
</div>
</form>
);
}
};
const modalClassName = show ? "add-task-modal display-block" : "add-task-modal display-none";
return (
<div className={modalClassName}>
<div className="add-task-modal-content">
{children}
<button type="button" id="close-btn" onClick={closeBtn}>
x
</button>
<Form></Form>
</div>
</div>
);
};
包含此表单的模态呈现在一个仅显示任务列表的文件中。 addTask()
方法成功地将表单数据传递给 'App.js',因为我可以直接更改状态,例如this.state.tasks = [...]
,但据我所知,这绝对不是最佳实践,我需要改用 setState()
。
编辑 2
因此,当我在改变状态的一个简单问题上跌跌撞撞时,开始变成了对 React 作为框架的简洁而有用的概述。非常感谢用户 Robin Zigmond 的撰写,并感谢 Alexander Staroselsky 进一步阐述了使用 redux 库的建议。您的意见帮助很大,我的应用程序的组件现在可以正确地相互通信。
只要说我最初的错误并不是 setState
的真正问题就够了,这有助于我在更广泛的层面上理解 React。再次感谢所有贡献的人。
你不应该就地改变数组,当你使用 push
对数组的引用不会改变。但不确定这是否与您的警告有关。
addTask(content, date, priority) {
this.setState({
tasks: this.state.tasks.concat({
content,
date,
priority
})
});
}
为什么用 new
调用 addTask
?你不应该这样做。
new App().addTask(this.state.content, this.state.date, this.state.highPriority);
可以使用context或redux
传递值和更新函数到模态。
您可以将 addTask 函数替换为:
addTask(content, date, priority) {
let task = {
content: content,
date: date,
priority: priority
};
this.setState({
tasks: [...this.state.tasks, task]
});
}
问题可以在您的 AddModal
组件中看到:
new App().addTask(this.state.content, this.state.date, this.state.highPriority);
手动为您的组件之一创建一个新实例 - 正如您在此处使用 new App()
所做的那样 - 不是您在 React 中永远需要做的事情。框架本身处理所有组件的生命周期——包括构建它们。
在这种情况下,警告本身就暴露了:“无法在尚未安装的组件上调用 setState”。 “mounted”是 React 中的一个概念,它仅指组件实例是否已呈现为用户界面的一部分——换句话说,它是否在页面上。 React 是一个 library/framework,其唯一目的是构建用户界面 - 每个组件都应该是您可以将 non-technical 用户指向 page/app 的一部分并说“那是此组件控制的部分”。在页面上拥有一个实际未呈现(或“挂载”以使用 React 术语)的组件实例没有任何意义。
从技术上讲,在准备好“挂载”组件之前,React 甚至不会调用您的 class 的构造函数 - 因此在调用构造函数之后,React 将调用您的组件的 render
方法然后(如果它有一个)它的 componentDidMount
方法。 (请参阅 React 组件生命周期图 here。)当您在 render
内的 JSX 中包含组件时,React 会自行处理所有这些 - 因此您不必担心。但是通过手动实例化 new App()
你是在构建一个组件实例而不安装它,React 不是设计来处理的,因此它有理由抱怨。
您似乎试图做的事情在更深层次上没有意义 - 因为您正在尝试更新组件状态,但组件不应该 状态尚未安装。同样,状态应该对应于 UI 中的某些内容,即使是在稍微抽象的层面上——它可能是用户当前在输入字段中输入的内容,或者更抽象的东西,比如标志某些动作已经完成或未完成,或者某个特定动作已经完成的次数 - 或者您想要的任何其他内容。但它对应于 something,并且该 something 与页面上组件内部发生的事情密切相关。如果它不在页面上 - 那么您的状态实际上是什么?
到 return 你实际应该做什么:我假设你的 App
是你应用程序的根组件,并且 AddModal
出现在它里面的某个地方。如果它是一个直接的 child,这将很简单,因为你的 App
总是在 AddModal
出现时呈现,你可以简单地将 addTask
方法作为 prop 传递下去。换句话说,在 App
中 render
内的某处,您将拥有:
<AddModal addTask={ this.addTask } />
然后在 AddModal
中称其为:
this.props.addTask(/*whatever arguments you need*/);
这会将 parent 组件的状态更新为您需要的状态。 (这里似乎正在更新 Sidebar
组件内的数据。)
如果看起来 AddModal
实际上下降了几级,则您必须将 addTask
下降几级。 IE。在 App
的 render
内,您将拥有
<SomeChildComponent addTask={ this.addTask } />
然后在SomeChildComponent
里面,说
<SomeOtherChild addTask={ this.props.addTask } />
依此类推,直到您最终达到
<AddModal addTask={ this.props.addTask } />
如果你有超过 1 或 2 个级别并且不需要中间级别的 addTask
,这肯定会变得乏味和脆弱 - 所以你可能想看看其他解决方案,如上下文, 或 Redux。但这就是你在“基本 React”中做事的方式,我认为你最好先理解这一点,然后再仔细研究其他模式。 (我坚信在开始使用 Redux 之类的库之前需要了解它们正在解决什么问题,因为“其他人都这样做”。)
我还建议阅读 this 以了解如何处理一个组件需要更新另一个不是直接 child 或 [=77 的状态的情况=] - 这似乎是你想要做的。
抱歉,我一直在胡说八道,但我希望这能让您更好地了解 React 的工作原理以及如何处理状态,以及为什么您不应该手动构建自己的组件。