React Hooks 场景下向state数组推入对象功能实现异常问题咨询
问题核心原因
- 组件不能声明为
async函数,React不支持异步组件直接导出,会导致渲染异常 useState未给初始值,productData初始状态为undefined,第一次更新时判空逻辑直接失效- 异步遍历用了
forEach,forEach不会等待异步回调执行完成,且回调中拿到的productData永远是第一次渲染的快照值,无法获取最新的状态 - 每次更新状态后你又额外执行了一次
setProductData([单个产品对象]),直接覆盖了之前累加的结果 - 渲染时直接访问
productData[0],未做非空判断,初始加载时会直接抛出类型错误
修复方案
调整状态初始化、异步遍历逻辑、状态更新方式后的可运行代码如下:
import { useState, useEffect } from 'react'; export const Component = () => { const productPages = ["testURL", "testURL", "testURL", "testURL"]; // 给状态设置初始空数组 const [productData, setProductData] = useState([]); useEffect(() => { const getData = async urls => { // 用for of代替forEach,保证异步操作按顺序执行 for (const url of urls) { try { const response = await fetch(url); const html = await response.text(); const productPage = document.createElement("div"); productPage.classList.add("hidden"); productPage.id = "no-visual"; productPage.innerHTML = html; productPage .querySelector('[data-test-id="main-price"]') .firstChild.remove(); productPage .querySelector('[data-test-id="main-price"]') .lastChild.remove(); productPage .querySelector('[data-test-id="second-price"]') .firstChild.remove(); productPage .querySelector('[data-test-id="second-price"]') .lastChild.remove(); const productName = productPage.querySelector( '[data-test-id="product-name"] h1' ).textContent; const productMainPrice = productPage.querySelector( '[data-test-id="main-price"]' ).textContent; const productSecondPrice = productPage.querySelector( '[data-test-id="second-price"]' ).textContent; const productQuantity = productPage.querySelector( '[data-test-id="button-wrapper"]' ).outerHTML; const productAddToBag = productPage.querySelector( '[data-test-id="button-wrapper"]' ).nextElementSibling.outerHTML; // 用函数式更新,获取最新的状态值,避免快照问题 setProductData(prev => [ ...prev, { productName, productMainPrice, productSecondPrice, productQuantity, productAddToBag } ]); } catch (err) { // 增加错误捕获,避免单个请求失败导致整个流程崩溃 console.error('页面请求失败', url, err); } } }; getData(productPages); }, []); // 渲染前增加非空判断,避免初始加载报错 if (!productData.length) return <div>加载中...</div>; return ( <div className="grid2x2"> <div className="box box1"> <div> <img src="" alt="" /> </div> <div> <div> {productData[0].productName} {productData[0].productMainPrice} <span> EX VAT </span> {productData[0].productSecondPrice} {productData[0].productQuantity} {productData[0].productAddToBag} <span> INC VAT </span> </div> </div> </div> </div> ); };
优化提示
如果需要提高请求效率,可以用Promise.all包裹所有请求,等所有请求返回后再一次性更新状态,减少重渲染次数。
内容的提问来源于stack exchange,提问作者Matthew Egan
相关产品推荐
相关产品推荐

