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
相关产品推荐
相关产品推荐

