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

测试React Admin组件时useGetRecordId获取记录ID失败求助

解决react-admin中useGetRecordId测试报错问题

问题背景

测试react-admin组件MostraLotePush时出现以下错误:

"useGetRecordId could not find the current record id. You need to use it inside a RecordContextProvider, or inside a supported route, or provide the record id to the hook yourself."

已尝试用<AdminContext>包裹组件、mock钩子,但均未解决问题。

组件代码

import React, { useState } from "react";
import { Button, Grid } from "@mui/material";
import {
  DateField,
  FunctionField,
  Labeled,
  Show,
  SimpleShowLayout,
  TextField,
  useGetRecordId,
  useNotify,
  useRecordContext,
  useRefresh,
} from "react-admin";
import cancelaLotePush from "../../api/cancelaLotePush";
import ChipStatus from "../../components/ChipStatus";

function MostraLotePush() {
  const [carregando, setCarregando] = useState(false);

  const notify = useNotify();
  const recordId = useGetRecordId();
  const refresh = useRefresh();

  const cancelarEnvio = async () => {
    setCarregando(true);
    try {
      await cancelaLotePush(recordId);
      refresh();
      notify(`Lote cancelado`, { type: "info" });
    } catch (e) {
      notify(`Erro: Não foi possível cancelar o envio do lote`, { type: "error" });
    }
    setCarregando(false);
  };

  const BotaoCancelarEnvio = () => {
    const record = useRecordContext();
    if (!record) return null;
    if (record.statusId !== 4) return null;
    return (
      <Button
        onClick={cancelarEnvio}
        sx={{ ml: 4, alignSelf: "flex-end", fontSize: 12 }}
        variant="outlined"
        color="error"
        size="small"
        disabled={carregando}
        data-testid="botao-cancelar"
      >
        Cancelar envio
      </Button>
    );
  };

  return (
    <Show title="Lote push">
      <SimpleShowLayout spacing={3}>
        <Grid container spacing={3}>
          <Grid item md={10} xs={12}>
            <Labeled label="Título">
              <TextField source="statusDescricao" />
            </Labeled>
          </Grid>
          <Grid item md={2} xs={12} marginTop={{ xs: 0, md: 2 }}>
            <FunctionField render={(record) => ChipStatus(record.statusDescricao, "medium")} />
          </Grid>
          <Grid item xs={12}>
            <Labeled label="Descrição">
              <TextField source="statusDescricao" />
            </Labeled>
          </Grid>
          <Grid item md={2} xs={12}>
            <Labeled label="Código">
              <TextField source="id" />
            </Labeled>
          </Grid>
          <Grid item md={4} xs={12}>
            <Labeled label="Data de envio">
              <DateField source="dataHoraInclusao" />
            </Labeled>
          </Grid>
          <Grid item md={6} xs={12} display="flex" alignItems="center">
            <Labeled label="Agendado para">
              <DateField source="dataHoraStatus" />
            </Labeled>
            <BotaoCancelarEnvio />
          </Grid>
        </Grid>
      </SimpleShowLayout>
    </Show>
  );
}

export default MostraLotePush;

测试代码

import { render } from "@testing-library/react";
import { AdminContext } from "react-admin";
import MostraLotePush from "../MostraLotePush";

describe("Monstra Lote Push", () => {
  it("Deveria renderizar corretamente", async () => {
    const { container } = render(
      <AdminContext
        dataProvider={{
          getOne: () =>
            Promise.resolve({ data: { id: 1, statusId: 4, statusDescricao: "Aguardando momento agendado" } }),
        }}
      >
        <MostraLotePush />
      </AdminContext>
    );
    expect(container).toMatchSnapshot();
  });
});

解决方案

问题核心是useGetRecordId需要同时在RecordContext和ResourceContext的包裹下才能正常工作,仅用AdminContext无法满足依赖。以下是三种可行方案:

方案1:补充上下文包裹(推荐)

直接用react-admin提供的上下文组件包裹测试组件,贴近真实运行环境:

import { render } from "@testing-library/react";
import { RecordContextProvider, ResourceContextProvider, AdminContext } from "react-admin";
import MostraLotePush from "../MostraLotePush";

describe("Monstra Lote Push", () => {
  it("Deveria renderizar corretamente", async () => {
    const record = { id: 1, statusId: 4, statusDescricao: "Aguardando momento agendado" };
    const { container } = render(
      <AdminContext
        dataProvider={{
          getOne: () => Promise.resolve({ data: record }),
        }}
      >
        <ResourceContextProvider name="lotePush">
          <RecordContextProvider value={record}>
            <MostraLotePush />
          </RecordContextProvider>
        </ResourceContextProvider>
      </AdminContext>
    );
    expect(container).toMatchSnapshot();
  });
});

方案2:Mock useGetRecordId钩子

直接mock目标钩子,快速绕过依赖限制:

import { render } from "@testing-library/react";
import { AdminContext } from "react-admin";
import MostraLotePush from "../MostraLotePush";

// Mock useGetRecordId钩子
jest.mock('react-admin', () => ({
  ...jest.requireActual('react-admin'),
  useGetRecordId: () => 1,
}));

describe("Monstra Lote Push", () => {
  it("Deveria renderizar corretamente", async () => {
    const { container } = render(
      <AdminContext
        dataProvider={{
          getOne: () =>
            Promise.resolve({ data: { id: 1, statusId: 4, statusDescricao: "Aguardando momento agendado" } }),
        }}
      >
        <MostraLotePush />
      </AdminContext>
    );
    expect(container).toMatchSnapshot();
  });
});

方案3:模拟路由参数

Show组件依赖路由中的id参数,用MemoryRouter模拟真实路由场景:

import { render } from "@testing-library/react";
import { AdminContext } from "react-admin";
import { MemoryRouter, Routes, Route } from "react-router-dom";
import MostraLotePush from "../MostraLotePush";

describe("Monstra Lote Push", () => {
  it("Deveria renderizar corretamente", async () => {
    const { container } = render(
      <MemoryRouter initialEntries={["/lotePush/1"]}>
        <AdminContext
          dataProvider={{
            getOne: () =>
              Promise.resolve({ data: { id: 1, statusId: 4, statusDescricao: "Aguardando momento agendado" } }),
          }}
        >
          <Routes>
            <Route path="/lotePush/:id" element={<MostraLotePush />} />
          </Routes>
        </AdminContext>
      </MemoryRouter>
    );
    expect(container).toMatchSnapshot();
  });
});

方案说明

  • 方案1最贴近生产环境,能覆盖更多组件逻辑测试;
  • 方案2适合快速编写快照测试,无需关注上下文细节;
  • 方案3适合测试路由相关的组件行为。

内容的提问来源于stack exchange,提问作者Gabriel Chiode

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 22:25:30