Here is a Solidity contract:
pragma solidity ^0.8.7;
contract SomeContract {
address public manager;
uint public theNumberNine;
mapping (string => mapping (address => uint)) public stringToAddrToInt;
constructor() {
manager = msg.sender;
theNumberNine = 9;
}
function setInt(string memory someString) public {
stringToAddrToInt[someString][msg.sender] = theNumberNine;
}
function getIntFromMapping(string memory someString) public view returns (uint) {
return stringToAddrToInt[someString][msg.sender];
}
}
Here is a JSX frontend:
import React, { Component } from 'react';
import { Button } from 'semantic-ui-react';
import SomeContract from 'someContract';
import Web3 from 'web3';
class SomeClass extends Component {
static async getInitialProps(props) {
const { address, myString } = props.query;
const contractInstance = SomeContract(address);
const provider = new Web3.providers.HttpProvider(
'https://kovan.infura.io/my-endpoint-here'
);
const web3 = new Web3(provider);
const accounts = await web3.eth.getAccounts();
await contractInstance.methods.setInt(myString).send({
from: accounts[0]
});
const intFromSolidity = await contractInstance.methods.getIntFromMapping(myString).call();
return { intFromSolidity };
}
onSubmit = async () => {
console.log(this.props.intFromSolidity);
}
render() {
return (
<Button basic onClick={this.onSubmit}>
I should output 9
</Button>
);
}
}
Expected output: 9
Actual output: undefined
The console log is undefined, but when calling getIntFromMapping in Remix, it outputs 9 as expected. I would appreciate a little enlightenment on the reason for this behavior, and how to properly return solidity mapping values for use in React components.
Depending on the version of next.js you're using, you may need to return from initialProps like this
return { props:{intFromSolidity} }Since you're using the next 12, getInitialProps might be causing problems. I made it a functional component and it doesn't need to use getInitialProps.
import { useEffect, useState } from "react"; const MyComponent = (props) => { //useEffect runs before compoent is loaded, so when loaded provider will be available const [provider, setProvider] = useState(null); const [accounts, setAccounts] = useState(null); const [solidityInt, setSolidityInt] = useState(null); const { address, myString } = props.query; useEffect(() => { const loadContract = async () => { setProvider( new Web3.providers.HttpProvider( "https://kovan.infura.io/my-endpoint-here" ) ); if (provider) { const web3 = new Web3(provider); const accounts = await web3.eth.getAccounts(); setAccounts(accounts); const contractInstance = SomeContract(address); await contractInstance.methods.setInt(myString).send({ from: accounts[0], }); const intFromSolidity = await contractInstance.methods .getIntFromMapping(myString) .call(); setSolidityInt(intFromSolidity); } }; await loadContract(); }, [provider]); const onSubmit = async () => { console.log(intFromSolidity); }; return ( <Button basic onClick={onSubmit}> I should output 9 </Button> ); };