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

如何让React状态在首次重渲染时及时更新

问题描述

我从API获取词典数据,经过洗牌和过滤后在页面展示随机单词。当前代码能跑,但实现方式不规范:首次通过普通变量设置当前单词,后续才用状态变量。

如果只使用state,首次点击按钮触发重渲染时会出现currentPage未定义的错误,单词显示不出来,但后续操作都正常。我猜这和useState的异步特性、状态变更与渲染时机有关,试过用useEffect(代码里已经注释掉了)但没用,想找个更优雅的实现方案。

原代码

import React, { useState, useEffect } from "react";
import Word from "./components/Word";

const options = {
  method: "GET",
  headers: {
    "X-RapidAPI-Key": "fd76888365mshc55b027be74dc4dp1d4f7ejsn88ecdcc39f3f",
    "X-RapidAPI-Host": "lexicala1.p.rapidapi.com",
  },
};

const App = () => {
  const [entryCount, setEntryCount] = useState(0);
  const [pageCount, setPageCount] = useState(1);
  const [currentPage, setCurrentPage] = useState("");
  const [currentWord, setCurrentWord] = useState("");

  // useEffect(() => {
  //   const word = document.querySelector('.word')
  //   word.textContent = currentWord;
  // });

  const shuffleResults = (array) => {
    for (let i = array.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      const temp = array[i];
      array[i] = array[j];
      array[j] = temp;
    }
    return array;
  };

  const filterResults = (words) => {
    words = words.results;
    const validPOS = ["noun", "verb", "adjective", "adverb"];
    words = words.filter((word) => validPOS.includes(word.headword.pos));
    return words;
  };

  const getDictionary = async () => {
    const response = await fetch(
      `https://lexicala1.p.rapidapi.com/search-entries?source=global&language=sv&page=${pageCount}&page-length=30&sample=30&text=`,
      options
    );
    let wordsData = await response.json();
    wordsData = filterResults(wordsData);
    wordsData = shuffleResults(wordsData);
    setCurrentPage(wordsData);
    return wordsData;
  };

  let page = "";

  const generateWord = async () => {
    if (!currentPage) {
      page = await getDictionary();
      setCurrentWord(page[entryCount].headword.text);
    } else setCurrentWord(currentPage[entryCount].headword.text);
    if (entryCount === currentPage.length - 1) {
      setPageCount(pageCount + 1);
      setEntryCount(0);
      setCurrentPage("");
    } else setEntryCount(entryCount + 1);
  };

  return (
    <div className="App">
      <button
        onClick={async () => {
          await generateWord();
        }}
      >
        hey
      </button>
      <Word word={currentWord} />
      <div className="word"></div>
    </div>
  );
};

export default App;

优化方案及代码

核心问题在于状态类型不一致(currentPage初始是空字符串,后续是数组)和闭包导致的状态过时,以下是修复后的优雅实现:

import React, { useState } from "react";
import Word from "./components/Word";

const options = {
  method: "GET",
  headers: {
    "X-RapidAPI-Key": "fd76888365mshc55b027be74dc4dp1d4f7ejsn88ecdcc39f3f",
    "X-RapidAPI-Host": "lexicala1.p.rapidapi.com",
  },
};

const App = () => {
  // currentPage初始化为空数组,保持类型一致
  const [entryCount, setEntryCount] = useState(0);
  const [pageCount, setPageCount] = useState(1);
  const [currentPage, setCurrentPage] = useState([]);
  const [currentWord, setCurrentWord] = useState("");

  const shuffleResults = (array) => {
    // 用解构避免修改原数组
    const shuffled = [...array];
    for (let i = shuffled.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
    }
    return shuffled;
  };

  const filterResults = (words) => {
    const validPOS = ["noun", "verb", "adjective", "adverb"];
    return words.results.filter(word => validPOS.includes(word.headword.pos));
  };

  const getDictionary = async (pageNum) => {
    const response = await fetch(
      `https://lexicala1.p.rapidapi.com/search-entries?source=global&language=sv&page=${pageNum}&page-length=30&sample=30&text=`,
      options
    );
    const wordsData = await response.json();
    const filtered = filterResults(wordsData);
    return shuffleResults(filtered);
  };

  const generateWord = async () => {
    // 统一处理数据获取逻辑
    let targetPage = currentPage;
    if (targetPage.length === 0) {
      targetPage = await getDictionary(pageCount);
      setCurrentPage(targetPage);
    }

    // 设置当前单词
    setCurrentWord(targetPage[entryCount].headword.text);

    // 用函数式更新避免闭包问题
    setEntryCount(prev => {
      // 判断是否需要加载下一页
      if (prev === targetPage.length - 1) {
        setPageCount(prevPage => prevPage + 1);
        setCurrentPage([]);
        return 0;
      }
      return prev + 1;
    });
  };

  return (
    <div className="App">
      <button onClick={generateWord}>获取随机单词</button>
      <Word word={currentWord} />
      {/* 移除冗余的DOM节点,完全通过组件props传递数据 */}
    </div>
  );
};

export default App;

关键改动说明

  1. 统一状态类型:把currentPage初始值设为[],避免空字符串和数组的类型混淆,解决首次加载时的未定义错误
  2. 避免闭包陷阱:使用setEntryCount(prev => ...)这种函数式更新方式,确保获取的是最新的状态值
  3. 简化逻辑:移除普通变量page,统一通过状态管理数据,避免混合状态和普通变量导致的不一致
  4. 纯函数优化:shuffleResults改用解构数组避免修改原数据,保持函数纯性
  5. 冗余代码清理:移除手动操作DOM的useEffect和多余的.word节点,完全通过React的props传递数据实现渲染

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 16:25:27