使用 Parse Server 返回的数据更新状态后,无法让 React 组件重新呈现
Can't get React component to re-render after state has been updated with returned data from Parse Server
我在使用状态数据渲染组件时遇到问题。我从 Parse Server 中提取数据,然后使用该数据更新状态。但是,我使用数据的组件(下面代码中的 <MenuItem />
)在数据可用之前呈现。
我试过这里描述的方法:
但是我无法让组件识别正在更新然后重新呈现的状态。
有什么想法吗?代码完整如下。还附上了一张屏幕截图,显示了成功的控制台日志和数据的存在状态。
(请注意,我是 React 的新手,所以很多代码可能不是最优的。)
import React from "react";
import Parse from 'parse';
import MenuItem from './MenuItem';
class MenuItems extends React.Component {
constructor() {
super();
this.loadMenuItems = this.loadMenuItems.bind(this);
this.state = {
menuItems: {},
order: {}
};
}
loadMenuItems() {
/* 0a. Get copy of State? */
const menuItems = {...this.state.menuItems};
/* 0. Get User */
// TODO: Get this from props
var user = Parse.User.current();
/* 1. Get Menu Items for this user */
var MenuItemTest = Parse.Object.extend('MenuItemTest');
var query = new Parse.Query(MenuItemTest);
query.equalTo('user', user);
query.find({
success: function(returnedMenuItems) {
console.log("Successfully retrieved " + returnedMenuItems.length + " menu items.");
for (var i = 0; i < returnedMenuItems.length; i++) {
var object = returnedMenuItems[i];
const menuItem = {
name: object.get('name'),
price: object.get('price'),
description: object.get('description'),
}
menuItems[`menu-item-${object.id}`] = menuItem;
}
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
this.setState({ menuItems });
}
componentWillReceiveProps(nextProps) {
console.log(nextProps);
this.loadMenuItems();
}
componentWillMount() {
this.loadMenuItems();
}
render() {
// Example from:
//
if (!this.state.response) {
return <div>Loading...</div>;
}
if (this.state.response.length === 0) {
return <div>No menu items yet</div>;
}
return (
<div className="menu-items">
{
Object
.keys(this.state.menuItems)
.map(key => <MenuItem key={key} details={this.state.menuItems[key]} />)
}
</div>
)
}
}
export default MenuItems;
更新代码以与评论线程一致。
import React from "react";
import Parse from 'parse';
import MenuItem from './MenuItem';
class MenuItems extends React.Component {
constructor() {
super();
this.loadMenuItems = this.loadMenuItems.bind(this);
}
loadMenuItems() {
const menuItems = {};
/* 0. Get User */
var user = Parse.User.current();
/* 1. Get Menu Items for this user */
var MenuItemTest = Parse.Object.extend('MenuItemTest');
var query = new Parse.Query(MenuItemTest);
query.equalTo('user', user);
query.find({
success: function(returnedMenuItems) {
console.log("Successfully retrieved " + returnedMenuItems.length + " menu items.");
for (var i = 0; i < returnedMenuItems.length; i++) {
var object = returnedMenuItems[i];
const menuItem = {
name: object.get('name'),
price: object.get('price'),
label: object.get('label'),
shorthand: object.get('shorthand'),
description: object.get('description')
}
menuItems[`menu-item-${object.id}`] = menuItem;
}
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
this.setState({ menuItems });
}
componentWillReceiveProps(nextProps) {
console.log('I ran');
console.log(nextProps);
this.loadMenuItems();
}
componentWillMount() {
this.loadMenuItems();
}
render() {
// Handle case where the response is not here yet
if (!this.state.menuItems) {
console.log('No state');
return <div>Loading...</div>;
}
// Gives you the opportunity to handle the case where the ajax request
// completed but the result array is empty
if (this.state.menuItems.length === 0) {
console.log('Zero items');
return <div>No menu items yet</div>;
}
if (this.state.menuItems) {
return (
<div className="menu-items">
{ console.log(this.state.menuItems) }
{ console.log(Object.keys(this.state.menuItems)) }
</div>
)
}
}
}
export default MenuItems;
就我从你这里得到的信息而言,
if (!this.state.response) {
return <div>Loading...</div>;
}
if (this.state.response.length === 0) {
return <div>No menu items yet</div>;
}
this.state.response
从未设置为状态,因此将始终显示 Loading...
我相信你想把它改成
if (!this.state.menuItems) {
return <div>Loading...</div>;
}
if (this.state.menuItems.length === 0) {
return <div>No menu items yet</div>;
}
并且您的 constructor
根本不设置 menuItems
状态(因此最初会显示加载)。
我找到了解决方案。虽然在尝试解决我的问题时,我移动了一些代码来设置父组件的状态并将其作为道具传递给 MenuItems。但是为什么它不起作用仍然是一样的。
与loadMenuItems()
有关:
loadMenuItems() {
const menuItems = {};
/* 0. Get User */
var user = Parse.User.current();
/* 1. Get Menu Items for this user */
var MenuItemTest = Parse.Object.extend('MenuItemTest');
var query = new Parse.Query(MenuItemTest);
query.equalTo('user', user);
query.find({
success: function(returnedMenuItems) {
console.log("Successfully retrieved " + returnedMenuItems.length + " menu items.");
for (var i = 0; i < returnedMenuItems.length; i++) {
var object = returnedMenuItems[i];
const menuItem = {
name: object.get('name'),
price: object.get('price'),
label: object.get('label'),
shorthand: object.get('shorthand'),
description: object.get('description')
}
menuItems[`menu-item-${object.id}`] = menuItem;
}
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
this.setState({ menuItems });
}
在执行 success
回调之前,this.setState({ menuItems })
是 运行。所以状态总是空的,但是当我检查控制台中的所有内容时,它已经返回并循环遍历 menuItems[
menu-item-${object.id}] = menuItem;
并且一切都在那里。
所以我所做的是在 success
回调中移动 this.setState({ menuItems });
然后添加 .bind(this)
这样我就可以在那里设置状态:
success: function(returnedMenuItems) {
...
}.bind(this),
而且我相信这些组件永远不会更新,因为虽然状态是用 menuItems[
menu-item-${object.id}] = menuItem;
循环设置的,但 this.setState()
从来没有再次调用,这就是触发组件刷新的原因。我可能对其中的某些内容不满意,但我认为这就是正在发生的事情。
我在下面发布了更新的代码以供将来参考。
父组件:
import React from "react";
import Parse from 'parse';
import MenuItems from './MenuItems';
class ManageMenu extends React.Component {
constructor() {
super();
this.loadMenuItems = this.loadMenuItems.bind(this);
}
loadMenuItems() {
const menuItems = {};
/* 0. Get User */
var user = Parse.User.current();
/* 1. Get Menu Items for this user */
var MenuItemTest = Parse.Object.extend('MenuItemTest');
var query = new Parse.Query(MenuItemTest);
query.equalTo('user', user);
query.find({
success: function(returnedMenuItems) {
console.log("Successfully retrieved " + returnedMenuItems.length + " menu items.");
for (var i = 0; i < returnedMenuItems.length; i++) {
var object = returnedMenuItems[i];
const menuItem = {
name: object.get('name'),
price: object.get('price'),
label: object.get('label'),
shorthand: object.get('shorthand'),
description: object.get('description')
}
menuItems[`menu-item-${object.id}`] = menuItem;
}
this.setState({ menuItems });
}.bind(this),
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
}
componentWillMount() {
this.loadMenuItems();
}
render() {
// Handle case where the response is not here yet
// console.log(this.state);
if (!this.state) {
console.log('No state');
// Note that you can return false it you want nothing to be put in the dom
// This is also your chance to render a spinner or something...
return <div>Loading...</div>;
}
if (this.state.menuItems) {
return (
<div className="manage-menu">
<div className="mt3 border-top">
<h3 className="mt1 bold s2">Menu Items</h3>
<div className="mt1">
<MenuItems menuItems={this.state.menuItems} />
</div>
</div>
</div>
)
}
}
}
export default ManageMenu;
菜单项组件:
import React from "react";
import MenuItem from './MenuItem';
class MenuItems extends React.Component {
render() {
// Same as: const menuItems = this.props.menuItems;
const { menuItems } = this.props;
// No menu items yet
if (menuItems.length === 0) {
console.log('Zero items');
return <div>No menu items yet</div>;
}
if (menuItems) {
return (
<div className="menu-items">
{
Object
.keys(menuItems)
.map(key => <MenuItem key={key} details={menuItems[key]} />)
}
</div>
)
}
}
}
export default MenuItems;
我在使用状态数据渲染组件时遇到问题。我从 Parse Server 中提取数据,然后使用该数据更新状态。但是,我使用数据的组件(下面代码中的 <MenuItem />
)在数据可用之前呈现。
我试过这里描述的方法:
但是我无法让组件识别正在更新然后重新呈现的状态。
有什么想法吗?代码完整如下。还附上了一张屏幕截图,显示了成功的控制台日志和数据的存在状态。
(请注意,我是 React 的新手,所以很多代码可能不是最优的。)
import React from "react";
import Parse from 'parse';
import MenuItem from './MenuItem';
class MenuItems extends React.Component {
constructor() {
super();
this.loadMenuItems = this.loadMenuItems.bind(this);
this.state = {
menuItems: {},
order: {}
};
}
loadMenuItems() {
/* 0a. Get copy of State? */
const menuItems = {...this.state.menuItems};
/* 0. Get User */
// TODO: Get this from props
var user = Parse.User.current();
/* 1. Get Menu Items for this user */
var MenuItemTest = Parse.Object.extend('MenuItemTest');
var query = new Parse.Query(MenuItemTest);
query.equalTo('user', user);
query.find({
success: function(returnedMenuItems) {
console.log("Successfully retrieved " + returnedMenuItems.length + " menu items.");
for (var i = 0; i < returnedMenuItems.length; i++) {
var object = returnedMenuItems[i];
const menuItem = {
name: object.get('name'),
price: object.get('price'),
description: object.get('description'),
}
menuItems[`menu-item-${object.id}`] = menuItem;
}
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
this.setState({ menuItems });
}
componentWillReceiveProps(nextProps) {
console.log(nextProps);
this.loadMenuItems();
}
componentWillMount() {
this.loadMenuItems();
}
render() {
// Example from:
//
if (!this.state.response) {
return <div>Loading...</div>;
}
if (this.state.response.length === 0) {
return <div>No menu items yet</div>;
}
return (
<div className="menu-items">
{
Object
.keys(this.state.menuItems)
.map(key => <MenuItem key={key} details={this.state.menuItems[key]} />)
}
</div>
)
}
}
export default MenuItems;
更新代码以与评论线程一致。
import React from "react";
import Parse from 'parse';
import MenuItem from './MenuItem';
class MenuItems extends React.Component {
constructor() {
super();
this.loadMenuItems = this.loadMenuItems.bind(this);
}
loadMenuItems() {
const menuItems = {};
/* 0. Get User */
var user = Parse.User.current();
/* 1. Get Menu Items for this user */
var MenuItemTest = Parse.Object.extend('MenuItemTest');
var query = new Parse.Query(MenuItemTest);
query.equalTo('user', user);
query.find({
success: function(returnedMenuItems) {
console.log("Successfully retrieved " + returnedMenuItems.length + " menu items.");
for (var i = 0; i < returnedMenuItems.length; i++) {
var object = returnedMenuItems[i];
const menuItem = {
name: object.get('name'),
price: object.get('price'),
label: object.get('label'),
shorthand: object.get('shorthand'),
description: object.get('description')
}
menuItems[`menu-item-${object.id}`] = menuItem;
}
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
this.setState({ menuItems });
}
componentWillReceiveProps(nextProps) {
console.log('I ran');
console.log(nextProps);
this.loadMenuItems();
}
componentWillMount() {
this.loadMenuItems();
}
render() {
// Handle case where the response is not here yet
if (!this.state.menuItems) {
console.log('No state');
return <div>Loading...</div>;
}
// Gives you the opportunity to handle the case where the ajax request
// completed but the result array is empty
if (this.state.menuItems.length === 0) {
console.log('Zero items');
return <div>No menu items yet</div>;
}
if (this.state.menuItems) {
return (
<div className="menu-items">
{ console.log(this.state.menuItems) }
{ console.log(Object.keys(this.state.menuItems)) }
</div>
)
}
}
}
export default MenuItems;
就我从你这里得到的信息而言,
if (!this.state.response) {
return <div>Loading...</div>;
}
if (this.state.response.length === 0) {
return <div>No menu items yet</div>;
}
this.state.response
从未设置为状态,因此将始终显示 Loading...
我相信你想把它改成
if (!this.state.menuItems) {
return <div>Loading...</div>;
}
if (this.state.menuItems.length === 0) {
return <div>No menu items yet</div>;
}
并且您的 constructor
根本不设置 menuItems
状态(因此最初会显示加载)。
我找到了解决方案。虽然在尝试解决我的问题时,我移动了一些代码来设置父组件的状态并将其作为道具传递给 MenuItems。但是为什么它不起作用仍然是一样的。
与loadMenuItems()
有关:
loadMenuItems() {
const menuItems = {};
/* 0. Get User */
var user = Parse.User.current();
/* 1. Get Menu Items for this user */
var MenuItemTest = Parse.Object.extend('MenuItemTest');
var query = new Parse.Query(MenuItemTest);
query.equalTo('user', user);
query.find({
success: function(returnedMenuItems) {
console.log("Successfully retrieved " + returnedMenuItems.length + " menu items.");
for (var i = 0; i < returnedMenuItems.length; i++) {
var object = returnedMenuItems[i];
const menuItem = {
name: object.get('name'),
price: object.get('price'),
label: object.get('label'),
shorthand: object.get('shorthand'),
description: object.get('description')
}
menuItems[`menu-item-${object.id}`] = menuItem;
}
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
this.setState({ menuItems });
}
在执行 success
回调之前,this.setState({ menuItems })
是 运行。所以状态总是空的,但是当我检查控制台中的所有内容时,它已经返回并循环遍历 menuItems[
menu-item-${object.id}] = menuItem;
并且一切都在那里。
所以我所做的是在 success
回调中移动 this.setState({ menuItems });
然后添加 .bind(this)
这样我就可以在那里设置状态:
success: function(returnedMenuItems) {
...
}.bind(this),
而且我相信这些组件永远不会更新,因为虽然状态是用 menuItems[
menu-item-${object.id}] = menuItem;
循环设置的,但 this.setState()
从来没有再次调用,这就是触发组件刷新的原因。我可能对其中的某些内容不满意,但我认为这就是正在发生的事情。
我在下面发布了更新的代码以供将来参考。
父组件:
import React from "react";
import Parse from 'parse';
import MenuItems from './MenuItems';
class ManageMenu extends React.Component {
constructor() {
super();
this.loadMenuItems = this.loadMenuItems.bind(this);
}
loadMenuItems() {
const menuItems = {};
/* 0. Get User */
var user = Parse.User.current();
/* 1. Get Menu Items for this user */
var MenuItemTest = Parse.Object.extend('MenuItemTest');
var query = new Parse.Query(MenuItemTest);
query.equalTo('user', user);
query.find({
success: function(returnedMenuItems) {
console.log("Successfully retrieved " + returnedMenuItems.length + " menu items.");
for (var i = 0; i < returnedMenuItems.length; i++) {
var object = returnedMenuItems[i];
const menuItem = {
name: object.get('name'),
price: object.get('price'),
label: object.get('label'),
shorthand: object.get('shorthand'),
description: object.get('description')
}
menuItems[`menu-item-${object.id}`] = menuItem;
}
this.setState({ menuItems });
}.bind(this),
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
}
componentWillMount() {
this.loadMenuItems();
}
render() {
// Handle case where the response is not here yet
// console.log(this.state);
if (!this.state) {
console.log('No state');
// Note that you can return false it you want nothing to be put in the dom
// This is also your chance to render a spinner or something...
return <div>Loading...</div>;
}
if (this.state.menuItems) {
return (
<div className="manage-menu">
<div className="mt3 border-top">
<h3 className="mt1 bold s2">Menu Items</h3>
<div className="mt1">
<MenuItems menuItems={this.state.menuItems} />
</div>
</div>
</div>
)
}
}
}
export default ManageMenu;
菜单项组件:
import React from "react";
import MenuItem from './MenuItem';
class MenuItems extends React.Component {
render() {
// Same as: const menuItems = this.props.menuItems;
const { menuItems } = this.props;
// No menu items yet
if (menuItems.length === 0) {
console.log('Zero items');
return <div>No menu items yet</div>;
}
if (menuItems) {
return (
<div className="menu-items">
{
Object
.keys(menuItems)
.map(key => <MenuItem key={key} details={menuItems[key]} />)
}
</div>
)
}
}
}
export default MenuItems;