You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中使用useState渲染子组件出现Hook调用顺序错误求助

解决React Hook调用顺序错误问题

问题场景

初始化空列表后通过useEffect从数据库获取数据填充,遍历列表渲染包含useState的子元素时,触发Hook调用顺序错误警告。

错误信息

Warning: React has detected a change in the order of Hooks called by Pedidos. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks

   Previous render            Next render
   ------------------------------------------------------
1. useContext                 useContext
2. useState                   useState
3. useEffect                  useEffect
4. undefined                  useState
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Pedidos@http://localhost:5173/src/ui/pages/logged/pedidos/pedidos.tsx?t=1667424544020:22:31
RenderedRoute@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=396bef63:2437:7
Routes@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=396bef63:2746:7
Router@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=396bef63:2697:7
BrowserRouter@http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=396bef63:3079:7
App@http://localhost:5173/src/App.tsx?t=1667424544020:30:35 react-dom.development.js:86:29

原代码

import "./../../../../assets/styles/logged/pedidos/pedidos.min.css"
import { Context } from "../../../../App";
import { useContext, useEffect, useState } from "react";
import { PedidoCamionInterface } from "../../../../domain/entities/pedido_camion/pedido_camion_interface";


export function Pedidos() {
    const providers = useContext(Context);
    const [pedidos, setPedidos] = useState<PedidoCamionInterface[]>([]);

    useEffect(() => {
        //TODO
        providers.providers.pedidosDb.getPedidosCamion([1, 2, 3], "").then((pedidos) => {
            setPedidos(pedidos);
        });
    }, []);

    return (
        <div id="content">
            <h1>Pedidos de camión</h1>
            <div id="pedidos">
                {pedidos.map((pedido: PedidoCamionInterface) => { return pedidoDiv(pedido); })}
            </div>
        </div>
    );
}


const pedidoDiv = (pedido: PedidoCamionInterface) => {
    const [selected, setSelected] = useState<boolean>(false);

    return (
        <div className={`pedido ${selected ? "selected" : ""}`} key={pedido.idPedido}>
            <span>{pedido.nombrePedido}</span>
            <span>{pedido.tienda}</span>
        </div>
    );
}

问题原因

pedidoDiv是普通工具函数,不是React组件。第一次渲染时pedidos为空数组,map不执行,pedidoDiv未被调用,因此不会触发内部的useState;当数据加载完成后,pedidos更新,map执行并调用pedidoDiv,此时新增了useState调用,导致父组件Pedidos的Hook调用顺序前后不一致,违反React Hooks的规则。

解决方案

将pedidoDiv改为标准React组件(组件名首字母大写),确保Hook在独立组件内部调用,不会影响父组件的Hook顺序:

import "./../../../../assets/styles/logged/pedidos/pedidos.min.css"
import { Context } from "../../../../App";
import { useContext, useEffect, useState } from "react";
import { PedidoCamionInterface } from "../../../../domain/entities/pedido_camion/pedido_camion_interface";


export function Pedidos() {
    const providers = useContext(Context);
    const [pedidos, setPedidos] = useState<PedidoCamionInterface[]>([]);

    useEffect(() => {
        providers.providers.pedidosDb.getPedidosCamion([1, 2, 3], "").then((pedidos) => {
            setPedidos(pedidos);
        });
    }, []);

    return (
        <div id="content">
            <h1>Pedidos de camión</h1>
            <div id="pedidos">
                {pedidos.map((pedido: PedidoCamionInterface) => { 
                    return <PedidoDiv key={pedido.idPedido} pedido={pedido} />; 
                })}
            </div>
        </div>
    );
}

// 改为首字母大写的React组件,props接收pedido
const PedidoDiv = ({ pedido }: { pedido: PedidoCamionInterface }) => {
    const [selected, setSelected] = useState<boolean>(false);

    return (
        <div className={`pedido ${selected ? "selected" : ""}`}>
            <span>{pedido.nombrePedido}</span>
            <span>{pedido.tienda}</span>
        </div>
    );
}

关键修改点:

  • 将pedidoDiv重命名为PedidoDiv(首字母大写,符合React组件命名规范)
  • 改为通过props接收pedido参数
  • 在父组件中以JSX标签形式渲染<PedidoDiv />,并将key属性移到组件标签上(key应该放在循环最外层的组件/元素上)

内容的提问来源于stack exchange,提问作者David Manzano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 08:01:02