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

使用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;
解决方案

核心问题分析

  1. 违反React钩子规则:在循环中调用useNFTCollection钩子,React要求钩子必须在组件顶层调用,不能放在条件、循环等代码块内,否则会导致组件渲染异常。
  2. 异步逻辑未正确处理:getNfts是异步函数,循环中直接调用后不会等待异步请求完成,console.log(dataArray)执行时请求还未结束,数组此时为空;后续异步完成后修改数组,但未触发组件重新渲染。
  3. 状态管理错误:直接修改普通变量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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 13:05:28