React报错:Rendered fewer hooks than expected 求助排查
问题描述
跟随freecodecamp的YouTube教程编写React代码,教程代码可正常运行,但我的代码出现报错:
Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement
原本期望渲染API返回的产品图片,却出现该错误,我的代码如下:
import React, { useEffect, useState } from "react"; import "./style.css"; export default function LoadMoreImages() { const [products, setProducts] = useState([]); const [count, setCount] = useState(0); const [loading, setLoading] = useState(false); const [disableButton, setDisableButton] = useState(false); async function fetchingProducts() { try { setLoading(true); const response = await fetch( `https://dummyjson.com/products?limit=20&skip=${ count === 0 ? 0 : count * 20 }` ); const result = await response.json(); if (result && result.products && result.products.length) { setProducts((prevData) => [...prevData, ...result.products]); setLoading(false); } console.log(result); } catch (e) { console.log(e); setLoading(false); } } useEffect(() => { fetchingProducts(); }, [count]); if (loading) { return <div>Loading Data! Please wait for sometime</div>; } useEffect(() => { if (products && products.length === 100) setDisableButton(true); }, [products]); return ( <div className="load-more-container"> <div className="product-container"> {products && products.length ? products.map((item) => ( <div className="product" key={item.id}> <img src={item.thumbnail} alt={item.title} /> <p>{item.title}</p> </div> )) : null} </div> <div className="button-container"> <button disabled={disableButton} onClick={() => setCount(count + 1)}> Load More Products </button> {disableButton ? <p>You have reached 100 products</p> : null} </div> </div> ); }
问题原因
React Hook的核心规则之一:所有Hook必须在组件的顶层作用域调用,不能放在条件判断、循环或者嵌套函数里。你的代码中,第二个useEffect被放在了if (loading) return语句之后——当loading为true时,组件会提前返回,这个useEffect根本不会被执行,导致组件每次渲染时调用的Hook数量不一致,触发了"Rendered fewer hooks than expected"错误。
修复方案
把第二个useEffect移到组件的顶层,放在所有条件判断语句之前,确保每次渲染时所有Hook都能被调用。修复后的代码如下:
import React, { useEffect, useState } from "react"; import "./style.css"; export default function LoadMoreImages() { const [products, setProducts] = useState([]); const [count, setCount] = useState(0); const [loading, setLoading] = useState(false); const [disableButton, setDisableButton] = useState(false); async function fetchingProducts() { try { setLoading(true); const response = await fetch( `https://dummyjson.com/products?limit=20&skip=${ count === 0 ? 0 : count * 20 }` ); const result = await response.json(); if (result && result.products && result.products.length) { setProducts((prevData) => [...prevData, ...result.products]); setLoading(false); } console.log(result); } catch (e) { console.log(e); setLoading(false); } } useEffect(() => { fetchingProducts(); }, [count]); // 移到组件顶层,确保每次渲染都会执行 useEffect(() => { if (products && products.length === 100) setDisableButton(true); }, [products]); if (loading) { return <div>Loading Data! Please wait for sometime</div>; } return ( <div className="load-more-container"> <div className="product-container"> {products && products.length ? products.map((item) => ( <div className="product" key={item.id}> <img src={item.thumbnail} alt={item.title} /> <p>{item.title}</p> </div> )) : null} </div> <div className="button-container"> <button disabled={disableButton} onClick={() => setCount(count + 1)}> Load More Products </button> {disableButton ? <p>You have reached 100 products</p> : null} </div> </div> ); }
额外优化建议
- 简化
products的判断:直接用{products.length ? ... : null}即可,因为useState初始化的是空数组,products永远不会是null或undefined。 - 在
fetchingProducts里添加产品数量判断,当已加载100个产品时直接终止请求,避免无效调用。
内容的提问来源于stack exchange,提问作者Junaid khan
相关产品推荐
相关产品推荐

