React Router结合Zustand引发渲染钩子数量异常问题求助
解决React Hook调用数量不一致的错误
问题根源
你的代码违反了React Hook的核心规则,导致渲染时Hook调用数量不稳定:
- 循环内调用
useState:const [edit, setEdit] = useState(false);写在了currentIncome.map的回调函数里。当数组长度变化(添加/删除收入项)时,Hook的调用数量会跟着改变,直接触发“渲染Hook数量与上一次不一致”的错误。 - 事件函数内调用自定义Hook:
useModifyData(id, "income", "delete")写在了deleteData这个事件处理函数里。Hook只能在组件顶层或自定义Hook顶层调用,不能在嵌套函数/事件逻辑内执行,这会破坏Hook的调用顺序。
修复方案
1. 拆分循环内逻辑为独立组件
创建单独的IncomeItem组件,把每个收入项的状态和操作逻辑封装进去,确保useState处于组件顶层:
// IncomeItem.tsx import { useState } from 'react' import Card from "~/components/tailwindcss/Card"; import useModifyData from "~/hooks/useModifyData"; import EditIncome from "~/components/income/EditIncome"; import SingleIncomeRender from "~/components/income/SingleIncomeRender"; type IncomeItemProps = { item: Income; colorOptions: { title: string; body: string; text: string; border: string; }; removeIncomeFromState: (id: number) => void; } function IncomeItem({ item, colorOptions, removeIncomeFromState }: IncomeItemProps) { const [edit, setEdit] = useState(false); const modifyData = useModifyData(); // 自定义Hook返回操作函数 const deleteData = () => { modifyData(item.id!, "income", "delete") removeIncomeFromState(item.id!) } const options = { id: item.id!, setEditing: () => setEdit(!edit), setDelete: deleteData, edit: edit, showEdit: true, } return ( <Card key={item.id} title={item.name} colorOptions={colorOptions} options={options}> {edit ? <EditIncome id={item.id!} setEditing={() => setEdit(!edit)}/> : <SingleIncomeRender id={item.id!} edit={edit}/>} </Card> ) } export default IncomeItem
2. 重构IncomeRender组件
修改原组件,调用拆分后的IncomeItem,避免在循环内直接使用Hook:
import IncomeItem from "./IncomeItem"; import {useIncomeStore} from "~/state/incomeStore"; function IncomeRender() { const currentIncome = useIncomeStore.use.currentIncome() const renderIncome = useIncomeStore.use.renderIncome() const removeIncomeFromState = useIncomeStore.use.removeIncome() const colorOptions = { title: "bg-green-700", body: "bg-green-100", text: "text-black", border: "border-green-700" } if (renderIncome) { return <div>Loading....</div> } return ( <div className="grid grid-cols-2 gap-4 place-items-center mb-5"> {currentIncome.map((item) => ( <IncomeItem key={item.id} item={item} colorOptions={colorOptions} removeIncomeFromState={removeIncomeFromState} /> ))} </div> ) } export default IncomeRender
3. 调整useModifyData的实现
确保自定义Hook返回可在事件中调用的函数,而非直接在事件内执行Hook逻辑:
// useModifyData.ts function useModifyData() { // 这里可以正常使用其他Hook(如useEffect、useState) const modify = (id: number, type: string, action: string) => { // 执行数据修改的具体逻辑 } return modify; } export default useModifyData;
核心规则回顾
React Hook必须遵守两个铁则:
- 只能在组件顶层或自定义Hook顶层调用
- 每次渲染时,Hook的调用顺序必须完全一致
按上述方案修改后,添加/删除数据时Hook的调用数量和顺序会保持稳定,错误即可解决。
内容的提问来源于stack exchange,提问作者Chris Williamson
相关产品推荐
相关产品推荐

