如何将状态管理(zustand)与tanstack table v8一起使用?

问题描述 投票:0回答:1

我有这家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'
        >
          &lt;
        </button>
        <button
          disabled={!table.getCanNextPage()}
          onClick={() => table.nextPage()}
          className='px-[0.5rem] py-[0.25rem] border border-gray-300 rounded'
        >
          &gt;
        </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'
        >
          &lt;
        </button>
        <button
          disabled={!table.getCanNextPage()}
          onClick={() => table.nextPage()}
          className='px-[0.5rem] py-[0.25rem] border border-gray-300 rounded'
        >
          &gt;
        </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;
javascript reactjs react-hooks zustand tanstack-table
1个回答
0
投票

Tanstack React-table 需要一个用于控制更改处理程序的函数,该函数接受值和更新程序作为其参数(想想 React 的 useState api)。

您从商店提供的设置器仅接受常规值,也不接受更新器功能。

对于常规用例,您可以执行类似的操作,例如分页:

 state: {
   pagination,
 },
 onPaginationChange: (updater) => {
   const newValue =
    updater instanceof Function ? updater(pagination) : updater;
  setSelected(newValue);
},

请参阅此处的相关文档:https://tanstack.com/table/latest/docs/framework/react/guide/table-state#2-updaters-can-either-be-raw-values-or-callback-功能

© www.soinside.com 2019 - 2024. All rights reserved.