如何解决有没有办法在 React 中显示结构的 Solidity 数组?
我正在尝试在 React 前端显示 solidity 结构数组的内容。
这是我的 solidity 智能合约。我创建了一个函数,它返回有问题的数组的长度。
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;
}
}
这是我的前端 React 代码:
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 子对象无效(找到:[object Promise])。如果您打算渲染一组子项,请改用数组。
阅读这里问题的答案,我认为问题可能是我需要异步调用我的 solidity 函数,以便我可以返回函数的输出而不是承诺。我试着像这样重写我的 React 代码:
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 子对象无效(找到:[object Promise])。如果您打算渲染一组子项,请改用数组。
如果有人可以提供任何建议,或者如果有人经历过类似的事情,我将不胜感激。我的目标是然后遍历数组以显示其所有元素,但到目前为止我什至似乎无法显示其中的元素数量。这特别奇怪,因为我能够通过 React 前端成功地从我的智能合约调用其他函数。非常感谢!
更新:
非常感谢 MrFrenzoid 的帮助。我重新编写了我的前端代码,以便我现在能够查询 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 = () => {
// 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() 时,我收到此错误消息:
我之所以采用这种方法,是因为我似乎无法一次查询整个 solidity 数组,而是需要一次查询一个元素。
更新:
在我重新部署运行本地区块链的 Truffle/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(),然后使用handler()函数强制handlePeople()等待,直到hanldePersonCount()完成之前被调用。可能有一种更简洁的方法来设置该顺序功能,但这似乎有效。
解决方法
为了回应您的最后一条评论,由于您已经在 js 中拥有数组,您可以快速执行 people.length
您也可以使用普通 for 对其进行迭代,
或一个
people.foreach((person) => { console.log(person); });
或
for( const person in people){
console.log(person);
}
如果您有任何问题,请告诉我,请在下方附上错误和您的代码。
更新 1(如何使用 web3 获取动态数组)。
试试这个: 创建一个状态变量来保存您的 peoples 数组,在定义时将其设为空数组。
然后,执行以下操作以迭代每个元素,并将它们堆叠起来。
// 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,将使solidity 隐式地为每个变量创建getter,因此如果您将变量设置为public,请不要费心创建getter,除非您想要什么在返回所述数据之前要做的事情,例如,跟踪人们访问所述变量的次数。
最好的问候!
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。