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
相关产品推荐
相关产品推荐

