我有这家zustand商店:
useStore.js:
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
export const useStore = create(
immer(set => ({
table: {
columns: [],
data: [],
globalFilter: '',
pagination: {},
setColumns: columns => set(s => { s.table.columns = columns }),
setData: data => set(s => { s.table.data = data }),
updateCell: (r, c, val) => set(s => { s.table.data[r][c] = val }),
addRow: () => set(s => {
const emptyRow = {};
s.table.columns.forEach(c => emptyRow[c.accessorKey] = '');
s.table.data.splice(
(s.table.pagination.pageIndex + 1) * s.table.pagination.pageSize - 1, 0, emptyRow
)
}),
setGlobalFilter: fs => set(s => { s.table.globalFilter = fs }),
setPagination: p => set(s => { s.table.pagination = p }),
}
}))
)
我正在尝试将其与 tanstack 表一起使用:
new.js:
import TABLE from '../../work.json';
import { useEffect, useState } from "react"
import * as XLSX from 'xlsx/xlsx.mjs';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faUpDown } from '@fortawesome/free-solid-svg-icons';
import { useStore } from '../../hooks/useStore';
import {
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable
} from '@tanstack/react-table';
function NikitaDev() {
const {
columns,
data,
globalFilter,
pagination,
setColumns,
setData,
addRow,
setGlobalFilter,
setPagination
} = useStore(s => s.table);
useEffect(() => {
setColumns(TABLE.headers.map(h => ({
accessorKey: h,
header: h,
cell: EditableCell
})));
setData(TABLE.data);
setPagination({
pageIndex: 0,
pageSize: 14
});
}, [])
const table = useReactTable({
data,
columns,
state: {
columnOrder: ["Stavební_díl-ID", "Allright_Stavebni_dil_ID"],
globalFilter,
pagination
},
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
columnResizeMode: 'onChange',
onPaginationChange: setPagination,
})
return (
<div className="flex flex-col gap-[1rem] m-[1rem]">
<div className="flex">
<input
value={globalFilter}
onChange={e => setGlobalFilter(e.target.value)}
placeholder='Search All columns'
className='flex-1 focus:outline-none'
/>
<button
onClick={() => {
const workbook = XLSX.utils.book_new();
const worksheet = XLSX.utils.json_to_sheet(data);
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet 1');
XLSX.writeFile(workbook, 'data.xlsx');
}}
className='bg-green-500 rounded text-white px-4 py-2'
>
Export to excel
</button>
</div>
<table className="w-full text-left text-sm text-gray-500">
<thead className="text-xs text-gray-700 uppercase bg-gray-50">
{table.getHeaderGroups().map(hg => (
<tr key={hg.id}>
{hg.headers.map(h => (
<th key={h.id} className='relative border px-6 py-3'>
{h.column.columnDef.header}
<button onClick={h.column.getToggleSortingHandler()}>
<span className='ml-[0.5rem]'>
{{
asc: " 🔼",
desc: " 🔽",
}[h.column.getIsSorted()] ||
<FontAwesomeIcon icon={faUpDown} />
}
</span>
</button>
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(r => (
<tr key={r.id}>
{r.getVisibleCells().map(c => (
<td key={c.id} className='border p-[0.25rem]'>
{flexRender(c.column.columnDef.cell, c.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
<span className='text-sm'>
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
</span>
<div className="flex gap-[0.5rem]">
<button
disabled={!table.getCanPreviousPage()}
onClick={() => table.previousPage()}
className='px-[0.5rem] py-[0.25rem] border border-gray-300 rounded'
>
<
</button>
<button
disabled={!table.getCanNextPage()}
onClick={() => table.nextPage()}
className='px-[0.5rem] py-[0.25rem] border border-gray-300 rounded'
>
>
</button>
<select
value={table.getState().pagination.pageSize}
onChange={e => {
table.setPageSize(Number(e.target.value))
}}
className='px-[0.5rem] py-[0.25rem] border border-gray-300 rounded'
>
{[10, 20, 30, 40, 50, 100, 200, 500].map(pageSize => (
<option key={pageSize} value={pageSize}>
{pageSize}
</option>
))}
</select>
<button
onClick={() => addRow()} // Call the addRow function when button is clicked
className='bg-blue-500 rounded text-white px-4 py-2 ml-2'
>
Add Row
</button>
</div>
</div>
)
}
function EditableCell({ getValue, row, column }) {
const updateCell = useStore(s => s.table.updateCell);
const initalValue = getValue();
const [value, setValue] = useState(initalValue);
useEffect(() => {
setValue(initalValue);
}, [initalValue])
function onChange(value) {
setValue(value);
updateCell(row.index, column.id, value);
}
return (
<input
type='text'
value={value}
onChange={e => onChange(e.target.value)}
className='w-full px-3 py-2'
/>
);
}
export default NikitaDev;
以前,我使用 useState 将其存储在本地,但也需要访问其他组件中的数据和列。问题。但现在,它没有渲染任何东西。这是没有 zustand 的版本:
old.js:
import TABLE from '../../work.json';
import { useEffect, useState } from "react"
import * as XLSX from 'xlsx/xlsx.mjs';
import {
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable
} from '@tanstack/react-table';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faUpDown } from '@fortawesome/free-solid-svg-icons';
function NikitaDev() {
const [columns, setColumns] = useState(() => {
return TABLE.headers.map(h => ({
accessorKey: h,
header: h,
cell: EditableCell
}));
});
const [data, setData] = useState(TABLE.data);
const [globalFilter, setGlobalFilter] = useState('');
const [pagination, setPagination] = useState({
pageIndex: 0,
pageSize: 14
});
const table = useReactTable({
data,
columns,
state: {
columnOrder: ["Stavební_díl-ID", "Allright_Stavebni_dil_ID"],
globalFilter,
pagination
},
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
columnResizeMode: 'onChange',
onPaginationChange: setPagination,
meta: {
updateData: (rowIndex, columnId, value) => setData(
prev => prev.map((row, index) => index === rowIndex ? {
...prev[index],
[columnId]: value
} : row)
),
addRow: () => {
const newData = [...data];
const emptyRow = {};
columns.forEach(col => {
emptyRow[col.accessorKey] = '';
});
newData.splice((pagination.pageIndex + 1) * pagination.pageSize - 1, 0, emptyRow); // Insert at the end of current page
setData(newData);
}
}
})
return (
<div className="flex flex-col gap-[1rem] m-[1rem]">
<div className="flex">
<input
value={globalFilter}
onChange={e => setGlobalFilter(e.target.value)}
placeholder='Search All columns'
className='flex-1 focus:outline-none'
/>
<button
onClick={() => {
const workbook = XLSX.utils.book_new();
const worksheet = XLSX.utils.json_to_sheet(data);
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet 1');
XLSX.writeFile(workbook, 'data.xlsx');
}}
className='bg-green-500 rounded text-white px-4 py-2'
>
Export to excel
</button>
</div>
<table className="w-full text-left text-sm text-gray-500">
<thead className="text-xs text-gray-700 uppercase bg-gray-50">
{table.getHeaderGroups().map(hg => (
<tr key={hg.id}>
{hg.headers.map(h => (
<th key={h.id} className='relative border px-6 py-3'>
{h.column.columnDef.header}
<button onClick={h.column.getToggleSortingHandler()}>
<span className='ml-[0.5rem]'>
{{
asc: " 🔼",
desc: " 🔽",
}[h.column.getIsSorted()] ||
<FontAwesomeIcon icon={faUpDown} />
}
</span>
</button>
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(r => (
<tr key={r.id}>
{r.getVisibleCells().map(c => (
<td key={c.id} className='border p-[0.25rem]'>
{flexRender(c.column.columnDef.cell, c.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
<span className='text-sm'>
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
</span>
<div className="flex gap-[0.5rem]">
<button
disabled={!table.getCanPreviousPage()}
onClick={() => table.previousPage()}
className='px-[0.5rem] py-[0.25rem] border border-gray-300 rounded'
>
<
</button>
<button
disabled={!table.getCanNextPage()}
onClick={() => table.nextPage()}
className='px-[0.5rem] py-[0.25rem] border border-gray-300 rounded'
>
>
</button>
<select
value={table.getState().pagination.pageSize}
onChange={e => {
table.setPageSize(Number(e.target.value))
}}
className='px-[0.5rem] py-[0.25rem] border border-gray-300 rounded'
>
{[10, 20, 30, 40, 50, 100, 200, 500].map(pageSize => (
<option key={pageSize} value={pageSize}>
{pageSize}
</option>
))}
</select>
<button
onClick={() => table.options.meta.addRow()} // Call the addRow function when button is clicked
className='bg-blue-500 rounded text-white px-4 py-2 ml-2'
>
Add Row
</button>
</div>
</div>
)
}
function EditableCell({ getValue, row, column, table }) {
const initalValue = getValue();
const [value, setValue] = useState(initalValue);
useEffect(() => {
setValue(initalValue);
}, [initalValue])
function onChange(value) {
setValue(value);
table.options.meta.updateData(row.index, column.id, value);
}
return (
<input
type='text'
value={value}
onChange={e => onChange(e.target.value)}
className='w-full px-3 py-2'
/>
)
}
export default NikitaDev;
Tanstack React-table 需要一个用于控制更改处理程序的函数,该函数接受值和更新程序作为其参数(想想 React 的 useState api)。
您从商店提供的设置器仅接受常规值,也不接受更新器功能。
对于常规用例,您可以执行类似的操作,例如分页:
state: {
pagination,
},
onPaginationChange: (updater) => {
const newValue =
updater instanceof Function ? updater(pagination) : updater;
setSelected(newValue);
},