使用Thirdweb获取NFT数组无长度且无法访问元素问题求助
问题:Next.js + Thirdweb 获取多合约NFT后无法访问数组内容
我正在用Next.js开发项目,想通过Thirdweb从不同合约里获取所有NFT:先从Sanity拿到合约地址(这步没问题),接着用useNFTCollection钩子调用getOwned方法获取NFT。
现在能看到dataArray里有NFT元素,但没法访问任何元素(一直返回undefined),也拿不到数组长度。
代码如下:
import React, { useState, useEffect } from "react"; import Header from "../../components/Header/Header"; import UserImages from "../../components/Profile/UserImages"; import UserInfo from "../../components/Profile/UserInfo"; import { client } from "../../lib/sanityClient"; import { useNFTCollection } from "@thirdweb-dev/react"; import { useRouter } from "next/router"; export async function getServerSideProps({}) { const query = `*[_type == "marketItems"] { contractAddress, }`; const data = await client.fetch(query); return { props: { data, }, }; } function profile({ data }) { const [nfts, setNfts] = useState({}); const router = useRouter(); const { wallet } = router.query; let dataArray = []; if (data[0] !== undefined) { if (dataArray === undefined || dataArray.length < 1) { dataArray = []; } for (let i = 0; i < data.length; i++) { const collection = useNFTCollection(data[i].contractAddress); async function getNfts() { if (collection !== undefined) { await collection.getOwned(wallet).then((res) => { Promise.all(res).then((values) => { for (let i = 0; i < values.length; i++) { dataArray.push(values[i]); } }); }); } } getNfts(); } console.log(dataArray); } return ( <div> <Header /> <UserImages /> <UserInfo /> </div> ); } export default profile;
解决方案
核心问题分析
- 违反React钩子规则:在循环中调用
useNFTCollection钩子,React要求钩子必须在组件顶层调用,不能放在条件、循环等代码块内,否则会导致组件渲染异常。 - 异步逻辑未正确处理:
getNfts是异步函数,循环中直接调用后不会等待异步请求完成,console.log(dataArray)执行时请求还未结束,数组此时为空;后续异步完成后修改数组,但未触发组件重新渲染。 - 状态管理错误:直接修改普通变量
dataArray而非React状态,组件无法感知数组变化,即使数组有内容也不会更新UI;初始状态nfts设为对象,不符合存储NFT数组的需求。
修复方案一:使用Thirdweb SDK直接调用(推荐)
绕过useNFTCollection钩子的限制,直接用Thirdweb SDK初始化合约实例,在useEffect中处理异步逻辑:
import React, { useState, useEffect } from "react"; import Header from "../../components/Header/Header"; import UserImages from "../../components/Profile/UserImages"; import UserInfo from "../../components/Profile/UserInfo"; import { client } from "../../lib/sanityClient"; import { ThirdwebSDK } from "@thirdweb-dev/sdk"; import { useRouter } from "next/router"; export async function getServerSideProps({}) { const query = `*[_type == "marketItems"] { contractAddress, }`; const data = await client.fetch(query); return { props: { data, }, }; } function Profile({ data }) { const [nfts, setNfts] = useState([]); const router = useRouter(); const { wallet } = router.query; useEffect(() => { if (!wallet || !data || data.length === 0) return; const fetchAllNFTs = async () => { try { // 替换为你的链ID,比如"polygon"、"ethereum"、"bnb"等 const sdk = new ThirdwebSDK("polygon"); let allNFTs = []; for (const contract of data) { if (!contract.contractAddress) continue; // 获取NFT合约实例 const collection = await sdk.getNFTCollection(contract.contractAddress); // 获取钱包地址拥有的NFT const ownedNFTs = await collection.getOwned(wallet); // 合并所有NFT到数组 allNFTs = [...allNFTs, ...ownedNFTs]; } // 更新React状态,触发组件重新渲染 setNfts(allNFTs); console.log("所有NFT:", allNFTs); } catch (error) { console.error("获取NFT失败:", error); } }; fetchAllNFTs(); }, [wallet, data]); // 现在可以正常访问nfts数组的内容和长度 console.log("NFT数量:", nfts.length); console.log("NFT详情:", nfts); return ( <div> <Header /> <UserImages /> <UserInfo /> {/* 示例:渲染所有NFT */} <div className="nft-container"> {nfts.map((nft, index) => ( <div key={index} className="nft-card"> <img src={nft.metadata.image} alt={nft.metadata.name} /> <h4>{nft.metadata.name}</h4> <p>{nft.metadata.description}</p> </div> ))} </div> </div> ); } export default Profile;
修复方案二:抽分子组件使用useNFTCollection钩子
如果必须使用useNFTCollection钩子,可将单个合约的NFT获取逻辑抽成子组件,父组件循环渲染子组件并汇总结果:
子组件(NFTCard.js)
import { useEffect } from "react"; import { useNFTCollection } from "@thirdweb-dev/react"; export default function NFTCard({ contractAddress, wallet, onNFTsLoaded }) { const collection = useNFTCollection(contractAddress); useEffect(() => { if (!collection || !wallet) return; const fetchNFTs = async () => { const ownedNFTs = await collection.getOwned(wallet); // 将获取到的NFT传递给父组件 onNFTsLoaded(ownedNFTs); }; fetchNFTs(); }, [collection, wallet, onNFTsLoaded]); return null; // 或单独渲染当前合约的NFT }
父组件(Profile.js)
import React, { useState } from "react"; import Header from "../../components/Header/Header"; import UserImages from "../../components/Profile/UserImages"; import UserInfo from "../../components/Profile/UserInfo"; import { client } from "../../lib/sanityClient"; import { useRouter } from "next/router"; import NFTCard from "./NFTCard"; export async function getServerSideProps({}) { const query = `*[_type == "marketItems"] { contractAddress, }`; const data = await client.fetch(query); return { props: { data, }, }; } function Profile({ data }) { const [nfts, setNfts] = useState([]); const router = useRouter(); const { wallet } = router.query; // 汇总子组件传递的NFT const handleNFTsLoaded = (newNFTs) => { setNfts((prev) => [...prev, ...newNFTs]); }; return ( <div> <Header /> <UserImages /> <UserInfo /> {/* 循环渲染子组件处理每个合约 */} {data.map((item, index) => ( <NFTCard key={index} contractAddress={item.contractAddress} wallet={wallet} onNFTsLoaded={handleNFTsLoaded} /> ))} {/* 渲染所有NFT */} <div className="nft-container"> {nfts.map((nft, idx) => ( <div key={idx} className="nft-card"> <img src={nft.metadata.image} alt={nft.metadata.name} /> <h4>{nft.metadata.name}</h4> </div> ))} </div> </div> ); } export default Profile;
内容的提问来源于stack exchange,提问作者Mixowo
相关产品推荐
相关产品推荐

