You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React web3 dApp首次加载不展示NFT映射元素 重点击按钮才正常渲染

问题根因

  • React的useState返回的状态更新函数是异步执行的,你在loadBlockchainData中执行完setCurrentAccount(accounts[0])后立刻调用loadContract,此时currentAccount还没有更新为真实的钱包地址,还是初始的0x0,导致loadContract里的判断currentAccount.length > 5不成立,首次加载时不会拉取NFT数据。第二次点击按钮时currentAccount已经是更新后的有效值,所以可以正常拉取展示。
  • setNFTModel函数中直接修改现有的model状态数组,属于直接修改React状态的非法操作,不仅可能导致重渲染异常,还会造成后续数据重复叠加的问题。
  • 组件内声明的临时变量myNFTs、supply等每次组件重渲染都会重置,不要用这种临时变量存储异步拉取的结果,全部放到函数作用域内处理即可。
  • 你查到的移除列表key属性的方案完全错误,React列表渲染必须设置唯一key,移除只会触发更多渲染报错,不要采用。

修复方案

直接替换对应逻辑即可,修改后的完整代码如下:

import { useState, useEffect } from 'react';
import Web3 from 'web3';
import axios from 'axios';
// 保留你原有其他依赖的引入,比如impContract、页面跳转函数等

export const AllCryptonauts = (props) => {
    const web3 = window.web3;
    const [currentAccount, setCurrentAccount] = useState("0x0");
    const [model, setModel] = useState([]);

    useEffect(() => {
        const init = async () => {
            window.ethereum.on('chainChanged', (_chainId) => checkChainID());
            window.ethereum.on('accountsChanged', (_accounts) => loadBlockchainData());
            await checkChainID();
        }
        init();

        return () => {
            // 移除事件监听避免内存泄漏
            window.ethereum.removeListener('chainChanged', checkChainID);
            window.ethereum.removeListener('accountsChanged', loadBlockchainData);
        }
    }, []) // 初始执行一次即可,不需要依赖currentAccount

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

    async function loadBlockchainData() {
        window.web3 = new Web3(window.ethereum);
        const accounts = await web3.eth.getAccounts();
        const activeAccount = accounts[0];
        setCurrentAccount(activeAccount);
        // 直接把拿到的真实地址传给loadContract,不依赖state的异步更新
        await loadContract(activeAccount);
    }

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

    async function setAllMints(supply) {
        // myNFTs放到函数内部,每次执行都新建数组,避免旧数据污染
        const myNFTs = [];
        const supplyNum = parseInt(supply, 10);
        for (let i = 1; i <= supplyNum; i++) {
            const 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) {
        // 直接返回新数组,不修改原状态
        return jsonObj.map(item => ({
            dna: item.dna,
            name: item.name,
            edition: item.edition,
            imagelink: item.imagelink,
            attributes: item.attributes
        }))
    }

    // 渲染逻辑保持原有不变即可
    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>
    )
}

内容的提问来源于stack exchange,提问作者Molly Hatchet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 22:36:05