Я столкнулся с проблемой в своем приложении 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
Привет, код включен. Пожалуйста ознакомтесь
Только что создал проект и скопировал этот код с github.com/adazzle/react-data-grid/blob/main/website/demos/…
Что именно не работает? в чем ошибка?
Включенные изображения, пожалуйста, проверьте. Если я запустил npm run dev, таблица будет отрисована идеально. Если я запустил npm run build и npm run start, пользовательский интерфейс выйдет из строя.





После создания нового проекта и рендеринга предоставленного вами кода я определил, что проблема связана с ошибкой гидратации.
Эта проблема возникает из-за несоответствия между деревом 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";
Кроме того, я заметил, что этот пример не импортирует таблицу стилей для библиотеки. Возможно, вы тоже захотите это сделать:
Извините, Фабио, внес изменения, ошибка гидратации исчезла. Но все еще сталкиваюсь с той же проблемой.
Кажется, проблема с пользовательским интерфейсом была исправлена после того, как удалось избежать замораживания, изменения размера и рендерингаEditCell из столбцов. Но мне нужны эти функции.
Конечно, посмотрим.
Сетка реагирующих данных построена с нулевым временем выполнения, поэтому возникает эта ошибка. Вам следует импортировать CSS-файл сетки данных глобально. В вите будет работать нормально.
Это не дает ответа на вопрос. Как только у вас будет достаточная репутация , вы сможете комментировать любую публикацию ; вместо этого дайте ответы, не требующие разъяснений от спрашивающего . - Из отзыва
Спасибо @Robin, я инициализировал CSS-сетку реагирующих данных глобально, и это работает.
В настоящее время работаю над проектом Next.js 14, используя [email protected], но у меня не возникает никаких проблем. Есть ли у вас репозиторий, которым вы можете поделиться? Или есть какие-то ошибки? «Это не работает» трудно определить.