首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >是否有一种方法来显示一个可靠的结构数组的反应?

是否有一种方法来显示一个可靠的结构数组的反应?
EN

Stack Overflow用户
提问于 2021-07-30 00:19:06
回答 1查看 2.7K关注 0票数 2

我试图在一个反应前端显示一个坚实的结构数组的内容。

这是我最可靠的合同。我创建了一个函数,它返回所述数组的长度。

代码语言:javascript
复制
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;
    }

}

以下是我的前端反应代码:

代码语言:javascript
复制
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函数,这样我就可以返回函数的输出,而不是承诺。我试着重写我的反应代码如下:

代码语言:javascript
复制
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函数甚至没有运行)。

然后,我尝试了另一种方法:

代码语言:javascript
复制
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的帮助。我已经重写了我的前端代码,以便我现在能够查询实体数组的各个元素:

代码语言:javascript
复制
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 (而不是像上面那样使用硬编码的值)。

代码语言:javascript
复制
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()。似乎我的错误是在智能契约中创建了一个新的函数/变量之后,没有重新部署本地块链。

下面是我的最后的、功能良好的前端代码:

代码语言:javascript
复制
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()之前等待完成。可能有一种更干净的方法来设置顺序的功能,但这似乎是可行的。

EN

回答 1

Stack Overflow用户

发布于 2021-07-31 16:29:53

针对您的最后一个注释,由于js中已经有了数组,您可以执行一个快速的people.length,您也可以使用普通的

或者是一个

代码语言:javascript
复制
people.foreach((person) => { console.log(person); });

代码语言:javascript
复制
for( const person in people){
   console.log(person);
}

如果您有任何问题,请让我知道,附加错误和您的代码下面。

更新1(如何使用web3获取动态数组)。

尝试如下:创建一个状态变量来保存您的人员数组,在定义它时使其成为一个空数组。

然后,执行以下操作,对每个元素进行迭代,并将它们堆叠起来。

代码语言:javascript
复制
// 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});

你的合同应该是这样的:

代码语言:javascript
复制
// 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,例如,跟踪人们访问所述变量的次数。

诚挚的问候!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68584090

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档