我试图在一个反应前端显示一个坚实的结构数组的内容。
这是我最可靠的合同。我创建了一个函数,它返回所述数组的长度。
pragma solidity ^0.8.0;
contract Project {
struct Person {
string name;
string description;
}
Person[] public people;
function getPersonCount() public view returns (uint) {
return people.length;
}
}以下是我的前端反应代码:
import React from "react";
import Web3 from './web3';
import { ABI } from './ABI';
import { contractAddr } from './Address';
const web3 = new Web3(Web3.givenProvider);
const ContractInstance = new web3.eth.Contract(ABI, contractAddr);
const NewPerson = () => {
let personCount = 0;
personCount = ContractInstance.methods.getPersonCount().call();
return (
personCount
);
};
export default NewPerson;当我运行前端代码时,我会收到以下错误消息:
错误:对象作为React子对象无效(找到:对象承诺)。如果您打算呈现一个子集合,请使用数组代替。
在阅读有关问题的答案时,我想问题可能是我需要异步调用我的Solidity函数,这样我就可以返回函数的输出,而不是承诺。我试着重写我的反应代码如下:
import React from "react";
import Web3 from './web3';
import { ABI } from './ABI';
import { contractAddr } from './Address';
const web3 = new Web3(Web3.givenProvider);
const ContractInstance = new web3.eth.Contract(ABI, contractAddr);
const NewPerson = () => {
let personCount = 0;
async function handlePerson() {
personCount = await ContractInstance.methods.getPersonCount().call();
}
handlePerson();
return (
personCount
);
};
export default NewPerson;这没有触发错误,而是返回0(这意味着handlePerson函数甚至没有运行)。
然后,我尝试了另一种方法:
import React from "react";
import Web3 from './web3';
import { ABI } from './ABI';
import { contractAddr } from './Address';
const web3 = new Web3(Web3.givenProvider);
const ContractInstance = new web3.eth.Contract(ABI, contractAddr);
const NewPerson = () => {
let personCount = 0;
async function handlePerson() {
personCount = await ContractInstance.methods.getPersonCount().call();
return (
personCount
);
}
return (
handlePerson()
);
};
export default NewPerson;这给了我第一次收到的同样的错误信息:
错误:对象作为React子对象无效(找到:对象承诺)。如果您打算呈现一个子集合,请使用数组代替。
如果有人能提供任何建议,或者有类似经历的人,我将不胜感激。我的目标是遍历数组来显示它的所有元素,但到目前为止,我似乎还无法显示其中的元素数量。这特别奇怪,因为我能够成功地通过React前端调用智能合同中的其他功能。事先非常感谢!
更新:
非常感谢MrFrenzoid的帮助。我已经重写了我的前端代码,以便我现在能够查询实体数组的各个元素:
import React from "react";
import Web3 from './web3';
import { ABI } from './ABI';
import { contractAddr } from './Address';
const web3 = new Web3(Web3.givenProvider);
const ContractInstance = new web3.eth.Contract(ABI, contractAddr);
const NewPerson = () => {
// Using hard-coded personCount for testing purposes
let personCount = 70;
let people = [];
async function handlePeople() {
for (let i=0; i<personCount; i++) {
const person = await ContractInstance.methods.people(i).call();
people.push(person);
}
console.log(people);
}
handlePeople();
return (
null
);
};
export default NewPerson;这将像预期的那样返回控制台中数组的内容。我仍然面临的问题是查询Solidity数组的长度,以便我可以使用它来计算personCount (而不是像上面那样使用硬编码的值)。
import React from "react";
import Web3 from './web3';
import { ABI } from './ABI';
import { contractAddr } from './Address';
const web3 = new Web3(Web3.givenProvider);
const ContractInstance = new web3.eth.Contract(ABI, contractAddr);
const NewPerson = () => {
let people = [];
async function handlePersonCount() {
const personCount = await ContractInstance.methods.getPersonCount().call();
console.log(personCount);
}
async function handlePeople() {
for (let i=0; i<personCount; i++) {
const person = await ContractInstance.methods.people(i).call();
people.push(person);
}
console.log(people);
}
handlePersonCount();
handlePeople();
return (
null
);
};
export default NewPerson;当我运行handlePersonCount()时,会收到以下错误消息:

我之所以采取这种方法,是因为我似乎无法一次查询整个实体数组,而需要一次查询一个元素。
更新:
一旦我重新部署了运行本地块链的to /Ganache实例,我就能够像预期的那样从前端调用getPersonCount()。似乎我的错误是在智能契约中创建了一个新的函数/变量之后,没有重新部署本地块链。
下面是我的最后的、功能良好的前端代码:
import React from "react";
import Web3 from './web3';
import { ABI } from './ABI';
import { contractAddr } from './Address';
const web3 = new Web3(Web3.givenProvider);
const ContractInstance = new web3.eth.Contract(ABI, contractAddr);
const NewPerson = () => {
let people = [];
async function handlePersonCount() {
const personCount = await ContractInstance.methods.getPersonCount().call();
console.log(personCount);
return(personCount);
}
async function handlePeople(qty) {
for (let i=0; i<qty; i++) {
const person = await ContractInstance.methods.people(i).call();
people.push(person);
}
console.log(people);
}
async function handler() {
await handlePeople(await handlePersonCount());
}
handler();
return (
null
);
};
export default NewPerson;我将返回行添加到handlePersonCount()中,以便将handlePersonCount()作为参数输入handlePeople(),然后使用处理程序()函数强制handlePeople()在调用hanldePersonCount()之前等待完成。可能有一种更干净的方法来设置顺序的功能,但这似乎是可行的。
发布于 2021-07-31 16:29:53
针对您的最后一个注释,由于js中已经有了数组,您可以执行一个快速的people.length,您也可以使用普通的
或者是一个
people.foreach((person) => { console.log(person); });或
for( const person in people){
console.log(person);
}如果您有任何问题,请让我知道,附加错误和您的代码下面。
更新1(如何使用web3获取动态数组)。
尝试如下:创建一个状态变量来保存您的人员数组,在定义它时使其成为一个空数组。
然后,执行以下操作,对每个元素进行迭代,并将它们堆叠起来。
// we create a local variable with the same name as the state variable where were going to hold our people elements.
let peopleArray = [];
// we query our counter.
const peopleCount = ContractInstance.methods.peopleCount().call();
// we iterate over each people and add it to our local variable.
// I start at 1 since in my contracts i first increment the counter
// and then do else, since the contract calls aren't mutex,
// theres a chance for users to be able to trigger a "race condition" by
// adding two "people" very fast, so fast that since the counter
// didn't update the first people was added, the second one will be
// processed with the same value as its counter, thats why, try
// counting first, and then doing anything else, its a good practice! :D.
for (var i = 1; i <= peopleCount ; i++) {
const person = ContractInstance.methods.people(i).call();
console.log(person);
peopleArray.push(person);
}
// we set the local varible value as our state variable, since it has the same name, you dont need to do {peopleArray: peopleArray}
this.setState({peopleArray});你的合同应该是这样的:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Project {
struct Person {
string name;
string description;
}
Person[] public people;
uint256 public peopleCount;
constructor(){
peopleCount = 0;
}
function addPerson(string memory name, string memory description) public returns (Person memory) {
// we do our corresponding checks.
require(bytes(name).length > 0, "Error: Dont leave the name empty!");
require(bytes(description).length > 0, "Error: Dont leave the description empty!");
// We increment the counter first so we avoid a race condition
peopleCount++;
// Create a person
Person memory p = Person(name, description);
// Push the person
people[peopleCount] = p;
// return the created person.
return p;
}}
请记住,将存储变量(如people和peopleCount等全局变量)设置为public时,每个变量都有可靠的创建getter,因此如果您的变量处于公共状态,则除非您希望在重新获取所述数据之前做一些事情,否则不要费心创建getter,例如,跟踪人们访问所述变量的次数。
诚挚的问候!
https://stackoverflow.com/questions/68584090
复制相似问题