Search code examples
reactjsreact-routerweb3-react

React page won't load mapped elements until calling them again


I have a React dApp for a smart contract that I have made. In one of the routes of the application which I go by clicking a button named "All Cryptonauts" as seen in the screenshow below, I try to call all of the minted NFT's of my smart contract. I can successfully get them and map them all, but at first, nothing comes up.

enter image description here

However, after clicking the "All Cryptonauts" button again, all of the intended data gets shown.

enter image description here

Below, there are the codes of my page. I think my problem is with rendering, so I have made some research and someone said that they avoid to manually rerender and fix an identical issue with removing the key attributes from the HTML codes, but it didn't work for me and there were errors in the console when I removed the keys. I can't use this.setState here, too. Can anyone help me with the right way to do what I want please? Thank you!

export const AllCryptonauts = (props) => {

    const web3 = window.web3;
    var [currentAccount, setCurrentAccount] = useState("0x0");
    let [model, setModel] = useState([]);
    let lastMintJson;
    let supply = [];
    let myNFTs = [];

    useEffect(() => {
        window.ethereum.on('chainChanged', (_chainId) => checkChainID());
        window.ethereum.on('accountsChanged', (_accounts) => loadBlockchainData());
        checkChainID();

        return () => { }
    }, [currentAccount])

    async function checkChainID() {
        const networkId = await web3.eth.net.getId();
        if (networkId !== 4) {
            props.history.push("/")
        } else {
            loadBlockchainData();
        }
    }

    async function loadBlockchainData() {

        window.web3 = new Web3(window.ethereum);
        const accounts = await web3.eth.getAccounts();
        setCurrentAccount(accounts[0]);
        loadContract();
    }

    async function loadContract() {
        if (currentAccount.length > 5) {
            const ContractObj = impContract;
            supply = await ContractObj.methods.totalSupply().call();
            setAllMints(supply);
        }
    }

    async function setAllMints(supply) {
        for (var i = 1; i <= parseInt(supply, 10); i++) {
            lastMintJson = "https://cors-anywhere.herokuapp.com/https://nftornek.000webhostapp.com/cryptonauts/json/" + i + ".json";
            let res = await axios.get(lastMintJson);
            res.data.imagelink = "https://nftornek.000webhostapp.com/cryptonauts/image/" + i + ".png"
            myNFTs.push(res.data);
        }
        setModel(setNFTModel(myNFTs));
    }

    function setNFTModel(jsonObj) {
        for (var i = 0; i < jsonObj.length; i++) {
            model[i] = {
                dna: jsonObj[i].dna,
                name: jsonObj[i].name,
                edition: jsonObj[i].edition,
                imagelink: jsonObj[i].imagelink,
                attributes: jsonObj[i].attributes
            };
        }
        return model;
    }

    return (
        <div>
            <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}><img src="https://nftornek.000webhostapp.com/frontend/cnlogo.png" width='500' height='180' alt=""></img></div>
            <div style={{ display: 'flex', justifyContent: 'center' }}>
                <button className="regularButton divide" onClick={MintPage}>Mint</button>
                <button className="regularButton divide" onClick={MyCryptonauts}>My Cryptonauts</button>
                <button className="regularButton divide" onClick={AllCryptonauts}>All Cryptonauts</button>
                <button className="regularButton divide" onClick={Disconnect}>Disconnect</button>
            </div>
            <div style={{ display: 'flex', justifyContent: 'center' }}><p className="accountText">Current Account: {currentAccount}</p></div>

            {model.map((item, i) => (
                <div key={i} style={{ display: 'flex', justifyContent: 'center', marginBottom: '30px', height: '350px' }}>
                    <div style={{ width: '350px', border: '2px solid #38495a', borderRadius: '5px' }}><img src={item.imagelink} alt=""></img>
                    </div>
                    <div style={{ width: '300px', padding: '10px', border: '2px solid #38495a', borderRadius: '4px', backgroundColor: 'rgba(56, 73, 90, 0.25)', color: '#38495a' }}><b>ID: {item.edition}<br></br> Name: {item.name}</b>
                        <table className="tableClass t1">
                            <tbody>
                                {item.attributes.map((attr, j) => (
                                    <tr key={'attr' + ' ' + j}>
                                        <td key={1}>{attr.trait_type}:</td>
                                        <td key={2}>{attr.value}</td>
                                    </tr>
                                ))}
                            </tbody>
                        </table></div>
                </div>
            ))}
        </div>
    )
}

Solution

  • I have fixed the error by following this answer by ray from Why is useState not triggering re-render?:

    You're calling setNumbers and passing it the array it already has. You've changed one of its values but it's still the same array, and I suspect React doesn't see any reason to re-render because state hasn't changed; the new array is the old array.

    One easy way to avoid this is by spreading the array into a new array:

    setNumbers([...old])
    

    Now it works.

    enter image description here