React中如何等待useState计数器更新完成后执行后续逻辑
问题根因
React的useState状态更新是异步批量执行的:调用setCount/setPage后不会立刻修改当前作用域下的状态变量,而是会将更新请求加入调度队列,等当前同步代码全部执行完成后再统一更新状态、触发组件重渲染。你原有代码中用Promise包裹setCount后立刻resolve,本质上还是同步执行逻辑,根本等不到状态更新完成,后续调用updateNews拿到的始终是更新前的旧状态值。
解决方案
有两种成熟的实现方式适配你的需求:
方案1:直接传递新值给业务方法
不用等待状态更新完成,计算出新的计数后直接传给updateNews使用,状态更新和业务逻辑执行互不阻塞:
const fetchMoreData = async () => { const newCount = count + 1; setCount(newCount); // 直接将新值传入updateNews,避免依赖旧的状态闭包 updateNews(newCount); };
方案2:用useEffect监听状态变化
将updateNews的触发和状态更新绑定,状态变更后自动触发业务逻辑,保证拿到的永远是最新的状态值:
const fetchMoreData = () => { // 用函数式更新避免闭包导致的状态计算错误 setCount(prev => prev + 1); }; // 监听count变化,count更新后自动执行updateNews useEffect(() => { // 可根据业务需求加判断,避免初始化时多余执行 if (count > 1) { updateNews(); } }, [count]);
适配你的新闻业务场景的修正代码
针对你提供的完整业务代码,推荐用方案2实现,修正后的核心逻辑如下:
import React, { useEffect, useState } from "react"; import NewsItem from "./NewsItem"; import Spinner from "./Spinner"; import PropTypes from "prop-types"; import InfiniteScroll from "react-infinite-scroll-component"; const News = (props) => { const [articles, setArticles] = useState([]); const [loading, setLoading] = useState(true); const [page, setPage] = useState(1); const [totalResults, setTotalResults] = useState(0); const capitalizeFirstLetter = (string) => { return string.charAt(0).toUpperCase() + string.slice(1); }; // updateNews直接使用最新的page状态 const updateNews = async () => { props.setProgress(10); const url = `newsapi.org/v2/…${props.country}&category=${props.category}&apiKey=${props.apiKey}&page=${page}&pageSize=${props.pageSize}`; props.setProgress(30); let data = await fetch(url); props.setProgress(50); let parsedData = await data.json(); props.setProgress(70); if (parsedData) { // 用函数式更新articles,避免闭包导致的旧数组拼接问题 setArticles(prevArticles => prevArticles.concat(parsedData.articles)); setLoading(false); setTotalResults(parsedData.totalResults); } props.setProgress(100); }; // 初始化加载第一页 useEffect(() => { updateNews(); // eslint-disable-next-line }, []); // 监听page变化,自动加载对应页数据 useEffect(() => { // 跳过第一页的重复执行 if (page > 1) { updateNews(); } }, [page]); const fetchMoreData = () => { // 只负责更新page,后续逻辑交给监听useEffect处理 setPage(prevPage => prevPage + 1); }; return ( <> <h3 className="text-center" style={{ marginTop: "4%" }}> NewsMonkey - Top `${capitalizeFirstLetter(props.category)}` Headlines </h3> {loading && <Spinner />} <InfiniteScroll dataLength={articles.length} next={fetchMoreData} hasMore={articles.length < totalResults} loader={<Spinner />} > <div className="container"> <div className="row"> {articles.map((element) => { return ( <div className="col-md-4" key={element.url}> <NewsItem title={ element && element.title ? element.title.slice(0, 45) : "" } description={ element && element.description ? element.description.slice(0, 50) : "" } imageUrl={element.urlToImage} newsUrl={element.url} author={element.author} date={element.publishedAt} source={element.source.name} /> </div> ); })} </div> </div> </InfiniteScroll> </> ); };
内容的提问来源于stack exchange,提问作者Himanshu Poddar
相关产品推荐
相关产品推荐

