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

函数组件:如何在useEffect及setState中获取最新state且不新增依赖

问题分析与解决方案

你的核心问题是React状态更新的异步性:调用setPreviousMovieList后,previousMovieList不会立刻更新,后续setMoviesList(previousMovieList.results)拿到的还是旧值(甚至初始的undefined),导致状态设为undefined引发报错。同时你的代码还有几个细节问题需要修正:

  1. 在setPreviousMovieList的函数式更新回调中调用setNextPage,违反React最佳实践(状态更新回调应仅负责计算新状态);
  2. else分支里setPreviousMovieList(previousMovieList)是无效操作,应该设置为初始的[data.data];
  3. 重复的API请求逻辑可以合并,减少冗余。

以下是几种可落地的修复方案:


方案一:先计算最新列表,同步更新状态

先算出previousMovieList的最新值,再直接用这个变量更新两个状态,完全规避异步状态更新的问题:

const [movies, setMoviesList] = useState([]);
const [currentGenre, setcurrentGenre] = useState();
const [page, setPage] = useState(1);
const [genreList, setGenreList] = useState();
const [nextPage, setNextPage] = useState(false);
const [previousMovieList, setPreviousMovieList] = useState([]); // 给初始空数组避免undefined

useEffect(() => {
  async function getMovies(currentGenre, page) {
    // 合并API请求地址逻辑
    let baseUrl = `https://api.themoviedb.org/3/discover/movie?api_key=f4872214e631fc876cb43e6e30b7e731&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=${page}`;
    const fullUrl = currentGenre ? `${baseUrl}&with_genres=${currentGenre}` : baseUrl;

    const data = await rawAxios.get(fullUrl);
    let newPreviousMovieList;

    if (nextPage) {
      // 加载下一页:追加新数据
      newPreviousMovieList = [...previousMovieList, data.data];
      setNextPage(false);
    } else {
      // 初始加载/切换分类/重置页面:替换为新数据
      newPreviousMovieList = [data.data];
    }

    // 同步更新两个状态
    setPreviousMovieList(newPreviousMovieList);
    // 根据需求选择:合并所有页的results / 只取最后一页的results
    setMoviesList(newPreviousMovieList.flatMap(item => item.results));
    // setMoviesList(newPreviousMovieList.at(-1).results);
  }

  getMovies(currentGenre, page);
}, [currentGenre, page, nextPage, previousMovieList]);

方案二:用useEffect监听状态变化自动同步

如果不想大幅改动现有逻辑,可以将setMoviesList移到专门监听previousMovieList的effect中,确保每次previousMovieList更新后自动同步:

const [movies, setMoviesList] = useState([]);
const [currentGenre, setcurrentGenre] = useState();
const [page, setPage] = useState(1);
const [genreList, setGenreList] = useState();
const [nextPage, setNextPage] = useState(false);
const [previousMovieList, setPreviousMovieList] = useState([]);

// 原effect只负责更新previousMovieList
useEffect(() => {
  async function getMovies(currentGenre, page) {
    let baseUrl = `https://api.themoviedb.org/3/discover/movie?api_key=f4872214e631fc876cb43e6e30b7e731&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=${page}`;
    const fullUrl = currentGenre ? `${baseUrl}&with_genres=${currentGenre}` : baseUrl;

    const data = await rawAxios.get(fullUrl);
    setPreviousMovieList(prev => {
      if (nextPage) {
        return [...prev, data.data];
      } else {
        return [data.data];
      }
    });

    if (nextPage) setNextPage(false);
  }

  getMovies(currentGenre, page);
}, [currentGenre, page, nextPage]);

// 监听previousMovieList变化,自动更新movies
useEffect(() => {
  if (previousMovieList.length > 0) {
    setMoviesList(previousMovieList.flatMap(item => item.results));
    // setMoviesList(previousMovieList.at(-1).results);
  }
}, [previousMovieList]);

方案三:移除冗余状态,直接派生数据

其实movies完全不需要单独存储,可直接从previousMovieList在渲染时派生,彻底避免状态不同步问题:

// 移除movies状态:const [movies, setMoviesList] = useState();
const [currentGenre, setcurrentGenre] = useState();
const [page, setPage] = useState(1);
const [genreList, setGenreList] = useState();
const [nextPage, setNextPage] = useState(false);
const [previousMovieList, setPreviousMovieList] = useState([]);

// 渲染时直接计算movies
const movies = previousMovieList.flatMap(item => item.results);
// 若只需展示最后一页:const movies = previousMovieList.at(-1)?.results || [];

// 原effect仅需维护previousMovieList即可
useEffect(() => {
  async function getMovies(currentGenre, page) {
    let baseUrl = `https://api.themoviedb.org/3/discover/movie?api_key=f4872214e631fc876cb43e6e30b7e731&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=${page}`;
    const fullUrl = currentGenre ? `${baseUrl}&with_genres=${currentGenre}` : baseUrl;

    const data = await rawAxios.get(fullUrl);
    setPreviousMovieList(prev => {
      if (nextPage) {
        setNextPage(false);
        return [...prev, data.data];
      } else {
        return [data.data];
      }
    });
  }

  getMovies(currentGenre, page);
}, [currentGenre, page, nextPage]);

额外注意事项

  • 给状态设置合理的初始值(比如空数组[]),避免undefined引发的报错;
  • 前端硬编码API Key存在泄露风险,建议通过后端代理转发API请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 04:30:58