Пользовательский интерфейс React Data Grid аварийно завершает работу после запуска сборки npm run и запуска npm run start

Я столкнулся с проблемой в своем приложении Next.js, из-за которой пользовательский интерфейс таблицы React Data Grid аварийно завершает работу после сборки и развертывания с помощью npm run build и npm run start. Во время разработки (npm run dev) все работает нормально. Мне нужна помощь в выявлении и решении этой проблемы.

Во время отладки npm запустите dev

После развертывания с помощью npm запустите build и npm run start

"use client";

import { useMemo, useState } from "react";
import { createPortal } from "react-dom";
import { faker } from "@faker-js/faker";
import "../../styles/globals.css";

import DataGrid, {
  SelectCellFormatter,
  SelectColumn,
  textEditor,
  type Column,
  type SortColumn,
} from "react-data-grid";

const dateFormatter = new Intl.DateTimeFormat("en");
const currencyFormatter = new Intl.NumberFormat("en", {
  style: "currency",
  currency: "eur",
});

interface SummaryRow {
  id: string;
  totalCount: number;
  yesCount: number;
}

interface Row {
  id: number;
  title: string;
  client: string;
  area: string;
  country: string;
  contact: string;
  assignee: string;
  progress: number;
  startTimestamp: number;
  endTimestamp: number;
  budget: number;
  transaction: string;
  account: string;
  version: string;
  available: boolean;
}

function getColumns(direction: any): readonly Column<Row, SummaryRow>[] {
  return [
    SelectColumn,
    {
      key: "id",
      name: "ID",
      frozen: true,
      resizable: false,
      renderSummaryCell() {
        return <strong>Total</strong>;
      },
    },
    {
      key: "title",
      name: "Task",
      frozen: true,
      renderEditCell: textEditor,
      renderSummaryCell({ row }) {
        return `${row.totalCount} records`;
      },
    },
    {
      key: "client",
      name: "Client",
      width: "max-content",
      draggable: true,
      renderEditCell: textEditor,
    },
    {
      key: "area",
      name: "Area",
      renderEditCell: textEditor,
    },
    {
      key: "contact",
      name: "Contact",
      renderEditCell: textEditor,
    },
    {
      key: "assignee",
      name: "Assignee",
      renderEditCell: textEditor,
    },
    {
      key: "progress",
      name: "Completion",
      renderCell(props) {
        const value = props.row.progress;
        return (
          <>
            <progress max = {100} value = {value} style = {{ inlineSize: 50 }} />{" "}
            {Math.round(value)}%
          </>
        );
      },
      renderEditCell({ row, onRowChange, onClose }) {
        return createPortal(
          <div
            dir = {direction}
            className = {"dialogContainerClassname"}
            onKeyDown = {(event) => {
              if (event.key === "Escape") {
                onClose();
              }
            }}
          >
            <dialog open>
              <input
                autoFocus
                type = "range"
                min = "0"
                max = "100"
                value = {row.progress}
                onChange = {(e) =>
                  onRowChange({ ...row, progress: e.target.valueAsNumber })
                }
              />
              <menu>
                <button type = "button" onClick = {() => onClose()}>
                  Cancel
                </button>
                <button type = "button" onClick = {() => onClose(true)}>
                  Save
                </button>
              </menu>
            </dialog>
          </div>,
          document.body
        );
      },
      editorOptions: {
        displayCellContent: true,
      },
    },
    {
      key: "startTimestamp",
      name: "Start date",
      renderCell(props) {
        return dateFormatter.format(props.row.startTimestamp);
      },
    },
    {
      key: "endTimestamp",
      name: "Deadline",
      renderCell(props) {
        return dateFormatter.format(props.row.endTimestamp);
      },
    },
    {
      key: "budget",
      name: "Budget",
      renderCell(props) {
        return currencyFormatter.format(props.row.budget);
      },
    },
    {
      key: "transaction",
      name: "Transaction type",
    },
    {
      key: "account",
      name: "Account",
    },
    {
      key: "version",
      name: "Version",
      renderEditCell: textEditor,
    },
    {
      key: "available",
      name: "Available",
      renderCell({ row, onRowChange, tabIndex }) {
        return (
          <SelectCellFormatter
            value = {row.available}
            onChange = {() => {
              onRowChange({ ...row, available: !row.available });
            }}
            tabIndex = {tabIndex}
          />
        );
      },
      renderSummaryCell({ row: { yesCount, totalCount } }) {
        return `${Math.floor((100 * yesCount) / totalCount)}% ✔️`;
      },
    },
  ];
}

function rowKeyGetter(row: Row) {
  return row.id;
}

function createRows(): readonly Row[] {
  const now = Date.now();
  const rows: Row[] = [];

  for (let i = 0; i < 1000; i++) {
    rows.push({
      id: i,
      title: `Task #${i + 1}`,
      client: faker.company.name(),
      area: faker.person.jobArea(),
      country: faker.location.country(),
      contact: faker.internet.exampleEmail(),
      assignee: faker.person.fullName(),
      progress: Math.random() * 100,
      startTimestamp: now - Math.round(Math.random() * 1e10),
      endTimestamp: now + Math.round(Math.random() * 1e10),
      budget: 500 + Math.random() * 10500,
      transaction: faker.finance.transactionType(),
      account: faker.finance.iban(),
      version: faker.system.semver(),
      available: Math.random() > 0.5,
    });
  }

  return rows;
}

type Comparator = (a: Row, b: Row) => number;

function getComparator(sortColumn: string): Comparator {
  switch (sortColumn) {
    case "assignee":
    case "title":
    case "client":
    case "area":
    case "country":
    case "contact":
    case "transaction":
    case "account":
    case "version":
      return (a, b) => {
        return a[sortColumn].localeCompare(b[sortColumn]);
      };
    case "available":
      return (a, b) => {
        return a[sortColumn] === b[sortColumn] ? 0 : a[sortColumn] ? 1 : -1;
      };
    case "id":
    case "progress":
    case "startTimestamp":
    case "endTimestamp":
    case "budget":
      return (a, b) => {
        return a[sortColumn] - b[sortColumn];
      };
    default:
      throw new Error(`unsupported sortColumn: "${sortColumn}"`);
  }
}

function Home() {
  const [rows, setRows] = useState(createRows);
  const [sortColumns, setSortColumns] = useState<readonly SortColumn[]>([]);
  const [selectedRows, setSelectedRows] = useState(
    (): ReadonlySet<number> => new Set()
  );

  const columns = useMemo(() => getColumns("ltr"), []);

  const summaryRows = useMemo((): readonly SummaryRow[] => {
    return [
      {
        id: "total_0",
        totalCount: rows.length,
        yesCount: rows.filter((r) => r.available).length,
      },
    ];
  }, [rows]);

  const sortedRows = useMemo((): readonly Row[] => {
    if (sortColumns.length === 0) return rows;

    return [...rows].sort((a, b) => {
      for (const sort of sortColumns) {
        const comparator = getComparator(sort.columnKey);
        const compResult = comparator(a, b);
        if (compResult !== 0) {
          return sort.direction === "ASC" ? compResult : -compResult;
        }
      }
      return 0;
    });
  }, [rows, sortColumns]);

  const gridElement = (
    <DataGrid
      rowKeyGetter = {rowKeyGetter}
      columns = {columns}
      rows = {sortedRows}
      defaultColumnOptions = {{
        sortable: true,
        resizable: true,
      }}
      selectedRows = {selectedRows}
      onSelectedRowsChange = {setSelectedRows}
      onRowsChange = {setRows}
      sortColumns = {sortColumns}
      onSortColumnsChange = {setSortColumns}
      topSummaryRows = {summaryRows}
      bottomSummaryRows = {summaryRows}
      className = "fill-grid"
      direction = "ltr"
    />
  );

  return (
    <div>
      <div className = {"toolbarClassname"}></div>
      {gridElement}
    </div>
  );
}

export default Home;

Что я пробовал:

  • Работать локально нормально означает, что когда я бегу npm run dev

  • Укажите, сохраняется ли проблема только после развертывания или локально.

Ожидаемый результат:

  • Должно быть так, как это работает локально.

Среда Next.js:

  • Реагирующая версия: 18

  • Версия Next.js: 14.1.0

  • версия сетки реагирующих данных: 7.0.0-beta.42

  • Менеджер пакетов: npm

В настоящее время работаю над проектом Next.js 14, используя [email protected], но у меня не возникает никаких проблем. Есть ли у вас репозиторий, которым вы можете поделиться? Или есть какие-то ошибки? «Это не работает» трудно определить.

Fabio Nettis 21.02.2024 15:59

Привет, код включен. Пожалуйста ознакомтесь

Abin Das 21.02.2024 16:09

Только что создал проект и скопировал этот код с github.com/adazzle/react-data-grid/blob/main/website/demos/…

Abin Das 21.02.2024 16:11

Что именно не работает? в чем ошибка?

Fabio Nettis 21.02.2024 16:17

Включенные изображения, пожалуйста, проверьте. Если я запустил npm run dev, таблица будет отрисована идеально. Если я запустил npm run build и npm run start, пользовательский интерфейс выйдет из строя.

Abin Das 21.02.2024 16:26
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
5
213
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

После создания нового проекта и рендеринга предоставленного вами кода я определил, что проблема связана с ошибкой гидратации.

Эта проблема возникает из-за несоответствия между деревом React, отображаемым во время первого рендеринга в браузере (называемым гидратацией), и деревом React, которое было предварительно визуализировано с сервера. В частности, эти несколько строк вызывают ошибку:

function createRows(): readonly Row[] {
  const now = Date.now();
  const rows: Row[] = [];

  for (let i = 0; i < 1000; i++) {
    rows.push({
      id: i,
      title: `Task #${i + 1}`,
      client: faker.company.name(),
      area: faker.person.jobArea(),
      // ....
    });
  }

  return rows;
}

Чтобы решить эту проблему, сгенерируйте строки с помощью Faker.js на сервере и передайте их компоненту <Home /> в качестве реквизита.

import "react-data-grid/lib/styles.css";

Кроме того, я заметил, что этот пример не импортирует таблицу стилей для библиотеки. Возможно, вы тоже захотите это сделать:

Извините, Фабио, внес изменения, ошибка гидратации исчезла. Но все еще сталкиваюсь с той же проблемой.

Abin Das 22.02.2024 05:34

Кажется, проблема с пользовательским интерфейсом была исправлена ​​после того, как удалось избежать замораживания, изменения размера и рендерингаEditCell из столбцов. Но мне нужны эти функции.

Abin Das 22.02.2024 06:53

Конечно, посмотрим.

Abin Das 22.02.2024 12:47
Ответ принят как подходящий

Сетка реагирующих данных построена с нулевым временем выполнения, поэтому возникает эта ошибка. Вам следует импортировать CSS-файл сетки данных глобально. В вите будет работать нормально.

Спасибо @Robin, я инициализировал CSS-сетку реагирующих данных глобально, и это работает.

Abin Das 17.04.2024 16:33

Другие вопросы по теме