React中函数内调用组件触发Invalid hook call错误求助
解决React组件Invalid hook call错误及状态管理问题
问题根源
- 错误调用组件:直接在
filterProductList里调用Products(false, newFilteredProducts),违反React组件使用规则——组件只能在JSX中渲染,不能像普通函数一样直接调用,会导致Hook调用违规。 - 状态分散管理:两个组件独立渲染,没有共享状态的通道,无法实现筛选后更新产品列表的联动。
- Hook使用违规:
Products组件里把useEffect嵌套在FetchData函数内部,React要求Hook必须在组件顶层调用。 - 组件参数接收错误:React组件的props是一个对象,直接用
start, listOfFilteredProducts接收参数,无法正确获取传入值。 - 全局数组滥用:未定义的全局
products数组直接push数据,不符合React状态更新逻辑。
修复方案:状态提升+统一根渲染
把产品数据、筛选逻辑的状态统一放到父组件中,让FilterMenu负责触发筛选操作,Products负责接收数据并渲染,通过父组件实现状态联动。
修改后的完整代码
import React, { useState, useEffect } from 'react'; import ReactDOM from 'react-dom/client'; // 请替换为实际的图片资源路径 import searchIcon from './assets/search-icon.png'; import cartImage from './assets/cart-icon.png'; function FilterMenu({ onFilter }) { return ( <div className="filterMenu"> <ul> <li><a>...</a></li> <li><a onClick={() => onFilter(null)}>ВСИЧКИ</a></li> <li><a onClick={() => onFilter('Гривни')}>ГРИВНИ</a></li> <li><a onClick={() => onFilter('ОГЪРЛИЦИ')}>ОГЪРЛИЦИ</a></li> <li><a onClick={() => onFilter('ПРЪСТЕНИ')}>ПРЪСТЕНИ</a></li> <li><a onClick={() => onFilter('ОБЕЦИ')}>ОБЕЦИ</a></li> <div className='filterSearchbar' id="filterSearchbar"> <input type="text" className='filterText' id="filterText" /> <img className="SearchIcon" src={searchIcon} /> </div> </ul> </div> ) } function Products({ productList }) { const items = productList.map((product, index) => ( <div className="grid-item" key={product.id || index + 1}> <div className="picture"> <img className="productPicture" src={'data:image/jpeg;base64,' + product.imageData} /> </div> <div className="title"><a>{product.name} {product.weight}гр</a></div> <div className="price"><a>{product.price} лв.</a></div> <div className="cartImage"> <img className="cart" src={cartImage} /> </div> </div> )); return ( <div className="grid-container"> {items} </div> ) } // 父组件统一管理状态 function App() { const [allProducts, setAllProducts] = useState([]); const [filteredProducts, setFilteredProducts] = useState([]); // 顶层调用useEffect获取API数据 useEffect(() => { const fetchProducts = async () => { try { const response = await fetch("http://localhost:5104/Product"); const data = await response.json(); setAllProducts(data); setFilteredProducts(data); // 初始显示所有产品 } catch (error) { console.error('获取产品数据失败:', error); } }; fetchProducts(); }, []); // 筛选函数,传给FilterMenu const handleFilter = (category) => { if (!category) { // 点击"ВСИЧКИ"时显示所有产品 setFilteredProducts(allProducts); return; } const newFiltered = allProducts.filter(product => product.category.includes(category)); setFilteredProducts(newFiltered); }; return ( <div> <FilterMenu onFilter={handleFilter} /> <Products productList={filteredProducts} /> </div> ) } // 统一渲染根组件 const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<App />);
关键修改点说明
- 新增父组件App:集中管理所有产品数据
allProducts和筛选后数据filteredProducts,负责API请求和筛选逻辑。 - FilterMenu改造:不再自行处理筛选状态,通过
onFilter回调把筛选条件传给父组件,由父组件更新状态。 - Products改造:变为纯展示组件,仅接收父组件传递的
productList并渲染,不再自行管理状态和API请求,避免Hook调用错误。 - 修正Hook使用:把API请求的
useEffect直接放在App组件顶层,符合React Hook规则。 - 统一根渲染:不再分别渲染两个组件,将App作为根组件渲染,确保组件树完整性和状态联动。
- 替换for循环为map:用React推荐的
map遍历数组生成元素,同时优先使用产品id作为key,无id则用index。
内容的提问来源于stack exchange,提问作者Руслан Василев
相关产品推荐
相关产品推荐

