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

修改UseState Hook后React组件未重新渲染问题求助

React状态更新后组件不渲染的问题排查与修复

我查了一堆相关资料都没解决这个问题——用useEffect Hook获取nftRewards对象数组,控制台能正常打印数据,但依赖这个状态的渲染部分在状态更新后完全没反应,根本不渲染。相关代码和未渲染的渲染片段如下:

原逻辑代码:

const [campaignRewardsJson, setCampaignRewardsJson] = useState<nftReward[]>([]);

const { data: rewards } = useContractRead({
  address: campaignsId,
  abi: campaignABI,
  functionName: 'getRewards',
});

async function getRewardObject() {
  var array: nftReward[] = [];
  rewards.map(async (reward, index) => {
    const json = fetch(reward[2]);
    const data = await json.then((res) => res.json());

    const nft: nftReward = {
      title: data.name,
      description: data.description,
      imageURL: data.image,
      price: reward[0],
      quantity: reward[1],
    };

    array.push(nft);
  });
  return array;
}

type nftReward = {
  title: string;
  description: string;
  imageURL: string;
  price: number;
  quantity: number;
};

useEffect(() => {
  if (rewards) {
    const fetchData = async () => {
      var data = await getRewardObject();
      setCampaignRewardsJson(data);
    };
    fetchData().catch(console.error);
  }
}, [rewards]);

未被渲染的代码片段:

{campaignRewardsJson.map((reward, index) => (
  <Card direction={{ base: 'column', sm: 'row' }} overflow="hidden" variant="outline">
    <Image objectFit="cover" maxW={{ base: '100%', sm: '200px' }} src={reward.imageURL} alt="Caffe Latte" />
    <Stack>
      <CardBody>
        <Heading size="md">{reward.title}</Heading>
        <Text py="2">{reward.description}</Text>
      </CardBody>
      <CardFooter>
        <Button variant="solid" colorScheme="blue">
          Mint NFT
        </Button>
      </CardFooter>
    </Stack>
  </Card>
))}

问题根源

核心问题在getRewardObject的异步处理逻辑:

  • rewards.map里用了async回调,map会直接返回一组Promise,但不会等待这些异步操作完成
  • 你在循环结束后直接返回array,此时所有fetch请求还在pending状态,array是空的,所以setCampaignRewardsJson设置的是空数组,渲染部分自然没有内容

修复方案

用Promise.all等待所有异步请求完成后再收集结果,修改后的核心代码如下:

修正后的getRewardObject函数

async function getRewardObject() {
  if (!rewards) return [];
  // 将每个异步请求包装成Promise,存入数组
  const promiseList = rewards.map(async (reward) => {
    const res = await fetch(reward[2]);
    const data = await res.json();
    return {
      title: data.name,
      description: data.description,
      imageURL: data.image,
      price: reward[0],
      quantity: reward[1],
    } as nftReward;
  });
  // 等待所有Promise完成,返回填充好的数组
  return await Promise.all(promiseList);
}

完整优化后的代码

// 类型定义移到组件外部,避免重复创建
type nftReward = {
  title: string;
  description: string;
  imageURL: string;
  price: number;
  quantity: number;
};

function YourComponent() {
  const [campaignRewardsJson, setCampaignRewardsJson] = useState<nftReward[]>([]);

  const { data: rewards } = useContractRead({
    address: campaignsId,
    abi: campaignABI,
    functionName: 'getRewards',
  });

  async function getRewardObject() {
    if (!rewards) return [];
    const promiseList = rewards.map(async (reward) => {
      const res = await fetch(reward[2]);
      const data = await res.json();
      return {
        title: data.name,
        description: data.description,
        imageURL: data.image,
        price: reward[0],
        quantity: reward[1],
      };
    });
    return await Promise.all(promiseList);
  }

  useEffect(() => {
    const fetchData = async () => {
      const data = await getRewardObject();
      setCampaignRewardsJson(data);
    };
    if (rewards) {
      fetchData().catch(console.error);
    }
  }, [rewards]);

  return (
    <div>
      {campaignRewardsJson.map((reward, index) => (
        {/* 必须添加key属性,符合React列表渲染规范 */}
        <Card key={index} direction={{ base: 'column', sm: 'row' }} overflow="hidden" variant="outline">
          <Image objectFit="cover" maxW={{ base: '100%', sm: '200px' }} src={reward.imageURL} alt={reward.title} />
          <Stack>
            <CardBody>
              <Heading size="md">{reward.title}</Heading>
              <Text py="2">{reward.description}</Text>
            </CardBody>
            <CardFooter>
              <Button variant="solid" colorScheme="blue">
                Mint NFT
              </Button>
            </CardFooter>
          </Stack>
        </Card>
      ))}
    </div>
  );
}

额外优化点

  • 给列表项添加key属性,这是React列表渲染的强制规范,避免不必要的性能问题
  • 将nftReward类型定义移到组件外部,避免组件每次渲染时重新创建类型
  • 给rewards增加空值判断,防止初始状态为undefined时触发报错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 00:20:32