我正在为研究目的建立一个基于React的项目。我坚持制作一个表组件,它呈现自己,然后向mbaas后端发送ajax请求以获取所有书籍条目并填写新行。这是我到目前为止所提出的。请原谅大块的代码,但由于我还没有完全理解方法之间的交互,因此state和render()在这里是整个类:
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调用deleteBook(id),这是BookTable的方法。在成功的ajax上,它应该从state.books(数组)和渲染中删除该书。
我对initBooks方法特别困惑。我在填充状态的循环之前添加了日志,并在状态更新时添加了回调。 log#1和log#3的结果相同,日志#2#4相同。此外,如果我展开log#2(在setState之前)或log#4,那么这两个都显示state = [1]。这有什么意义?此外,如果您查看#1#3日志 - 它们会打印[]。我可能错过了一些内部组件交互,但我真的无法弄清楚是什么。
感谢。
答案 0 :(得分:1)
setState不会立即更新状态。因此,在for循环的第二次迭代中,您将不会获得新状态。因此,首先制作新书列表,然后在准备好新列表后进行设置。
试试这个:
<base href="/"/>
答案 1 :(得分:1)
试试这个:
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
方法可以返回现有值。