React组件问题与状态,setState和渲染
我正在构建一个基于React的项目用于学习目的。我被困在制作一个表格组件,它呈现自己,然后向mbaas后端发送一个Ajax请求,以获取所有书籍条目并将每个条目填充到新行中。这是我到目前为止所做的。请原谅大块的代码,但因为我还没有完全理解的方法,状态和渲染(之间的相互作用),这里是整个类:React组件问题与状态,setState和渲染
class BooksTable extends Component {
constructor(props) {
super(props);
this.state = {
books: []
};
this.updateState = this.updateState.bind(this);
this.initBooks = this.initBooks.bind(this);
}
componentDidMount() {
let method = `GET`;
let url = consts.serviceUrl + `/appdata/${consts.appKey}/${consts.collection}`;
let headers = {
"Authorization": `Kinvey ${sessionStorage.getItem(`authToken`)}`,
"Content-Type": `application/json`
};
let request = {method, url, headers};
$.ajax(request)
.then(this.initBooks)
.catch(() => renderError(`Unable to connect. Try again later.`));
}
deleteBook(id) {
let method = `DELETE`;
let url = consts.serviceUrl + `/appdata/${consts.appKey}/${consts.collection}/${id}`;
let headers = {
"Authorization": `Kinvey ${sessionStorage.getItem(`authToken`)}`,
"Content-Type": `application/json`
};
let request = {method, url, headers};
$.ajax(request)
.then(() => this.updateState(id))
.catch(() => renderError(`Unable to delete, something went wrong.`));
}
updateState(id) {
for (let entry of this.state.books.length) {
if (entry.id === id) {
// Pretty sure this will not work, but that is what I've figured out so far.
this.state.books.splice(entry);
}
}
}
initBooks(response) {
console.log(`#1:${this.state.books});
console.log(`#2:${this});
for (let entry of response) {
this.setState({
books: this.state.books.concat([{
id: entry._id,
name: entry.name,
author: entry.author,
description: entry.description,
price: Number(entry.name),
publisher: entry.publisher
}])
},() => {
console.log(`#3${this.state.books}`);
console.log(`#4${this}`);
});
}
}
render() {
return (
<div id="content">
<h2>Books</h2>
<table id="books-list">
<tbody>
<tr>
<th>Title</th>
<th>Author</th>
<th>Description</th>
<th>Actions</th>
</tr>
{this.state.books.map(x =>
<BookRow
key={x.id}
name={x.name}
author={x.author}
description={x.description}
price={x.price}
publisher={x.publisher} />)}
</tbody>
</table>
</div>
);
}
}
现在BookRow是不是很有趣,只有onClick部分是相关的。它看起来像这样:
<a href="#" onClick={() => this.deleteBook(this.props.id)}>{owner? `[Delete]` : ``}</a>
如果登录用户不是本书的出版者,则链接不应该是可见的。 onClick调用来自BookTable的方法deleteBook(id)。在成功的ajax上,它应该从state.books(数组)和render中删除这本书。
我对initBooks方法特别困惑。我在填充状态的循环之前添加了日志,并在状态更新时作为回调。日志#1和日志#3的结果相同,日志#2#4的结果相同。另外,如果我扩展日志#2(在setState之前)或日志#4,那么这两个显示状态= [1]。这有什么意义?此外,如果您查看日志#1#3 - 他们打印[]。我可能错过了一些内部组件交互,但我真的不知道是什么。
谢谢。
setState不会立即更新状态。所以在for循环的第二次迭代中,你不会得到新的状态。因此,请先制作新的图书清单,然后在新清单准备好后再进行设置。
试试这个:
initBooks(response) {
console.log(this.state.books, "new books not set yet")
let newBooks = []
for (let entry of response) {
newBooks.push({
id: entry._id,
name: entry.name,
author: entry.author,
description: entry.description,
price: Number(entry.name),
publisher: entry.publisher
})
}
this.setState({books: [...this.state.books, newBooks]},() => {
console.log(this.state.books, "new books set in the state")
})
}
感谢您的回答。 @自由的灵魂,也非常感谢。我会试一试。问题是,在我目前的测试中,循环只进行一次,因为我只有一个测试记录。在循环中设置状态是愚蠢的,我应该想到这一点,但它仍然感到奇怪。让我试试看,回到你身边。 – Alex
如果循环只进行一次,那么它应该工作。我复制了你的代码并试了一下。它对我很有用 – Swapnil
正如我的想法 - 虽然你的意思是将setState从循环中移出很好,但它并不能帮助我解决问题。运行你的代码 - 它不会打印'在该州设置的新书'。我只收到有关重要财产的警告。我仍然无法解释日志#1#2如何输出不同的结果,因为它们正在记录相同的对象。为什么打印对象显示长度为1的更新状态并且包含从服务器响应接收到的对象。我甚至没有初始化临时阵列持有人呢.. – Alex
试试这个:
initBooks(response = {}) {
const books = Object.keys(response);
if (books.length > 0) {
this.setState((prevState) => {
const newBooks = books.reduce((acc, key) => {
const entry = response[key];
return [ ...acc, {
id: entry._id,
name: entry.name,
author: entry.author,
description: entry.description,
price: Number(entry.name),
publisher: entry.publisher
}];
}, prevState.books);
return { books: newBooks };
});
}
}
我在这里做什么?
-
setState
仅在需要时(即仅当存在来自API响应数据) - 避免状态突变,无
setState
内使用功能((prevState, props) => newState
),以确保可靠性原子 更新循环 - 。
的考虑点:
- 不要发生变异的状态
避免
setState
一个循环中;如果你想同时呼吁setState
访问以前的状态,而不是准备新的状态对象,做一个一次性setState
)-
,它是advised做这样的:
this.setState((prevState, props) => { return { counter: prevState.counter + props.increment }; });
setState()
不立即变异this.state
,但创建一个 待处理状态转换。调用此 方法后访问this.state
可能会返回现有值。
好的提示总结。谢啦。 – Alex
的setState动作是异步的,所以更改可能不会在this.state反映的时候了。 –