iT邦幫忙

2022 iThome 鐵人賽

DAY 17
0

Day-17 打造表格列選擇功能 react-table

在 todos 中可以當作任務是否完成的選取框框,也可以作為列的選取手段,這樣的設計好像很少見,我之前其實沒有用過 react-table 來做列選擇功能,所以這對我來說也是第一次,十分新鮮。這個功能是用選取框框勾選的方式來選到表格的一列,能夠一列一列選,也能夠全部一起選。

引入必要的工具

繼續完成 TodoPge 中表格選取的功能吧,先從 react-table 引入必要的工具 :

import {
  useReactTable, // 掌管整個 table 的重要 hook
  getCoreRowModel, // 表格 row 的核心模型
  flexRender, // 用來渲染 flex box
} from "@tanstack/react-table";

根據前一天提到的事情,我們會需要給 table 輸入 data 跟 columns,先來定義好 :

const [data, setData] = useState(() => [...list]);
const columns = useMemo(
  () => [
    {
      id: "select",
      header: ({ table }) => (
        <IndeterminateCheckbox
          className="form-input border-[2px] rounded border-gray-300 w-[20px] h-[20px]"
          {...{
            checked: table.getIsAllRowsSelected(),
            indeterminate: table.getIsSomeRowsSelected(),
            onChange: table.getToggleAllRowsSelectedHandler(),
          }}
        />
      ),
      cell: ({ row }) => (
        <div className="px-1">
          <IndeterminateCheckbox
            className="form-input border-[2px] rounded border-gray-300 w-[20px] h-[20px]"
            {...{
              checked: row.getIsSelected(),
              indeterminate: row.getIsSomeSelected(),
              onChange: row.getToggleSelectedHandler(),
            }}
          />
        </div>
      ),
    },
    { header: "任務標題", accessorKey: "title" },
    { header: "詳情", accessorKey: "info" },
  ],
  []
);

原本我使用 chakra UI 的 CheckBox,不過我在這裡改用官方網站提供的範例來做,變回使用原生的 input :

function IndeterminateCheckbox({
    indeterminate,
    className = "",
    ...rest
  }: { indeterminate?: boolean } & HTMLProps<HTMLInputElement>) {
    const ref = useRef<HTMLInputElement>(null!);

    useEffect(() => {
      if (typeof indeterminate === "boolean") {
        ref.current.indeterminate = !rest.checked && indeterminate;
      }
    }, [ref, indeterminate]);

    return (
      <input
        type="checkbox"
        ref={ref}
        className={className + " cursor-pointer"}
        {...rest}
      />
    );
  }

不過我有自己把樣式重寫成自己喜歡的樣子:P 嘿嘿 :

<IndeterminateCheckbox
            className="form-input border-[2px] rounded border-gray-300 w-[20px] h-[20px]" // 這裡
            {...{
              checked: row.getIsSelected(),
              indeterminate: row.getIsSomeSelected(),
              onChange: row.getToggleSelectedHandler(),
            }}
          />
        </div>

之後把它們丟進 useReactTable() 中初始化 :

const table = useReactTable({
  data,
  columns,
  getCoreRowModel: getCoreRowModel(),
});

接下來就可以嘗試改用 react-table 的方式輸出表格 :

...
        <Thead>
            {table.getHeaderGroups().map((headerGroup) => (
                <Tr>
                  {headerGroup.headers.map((header) => (
                    <Th>
                      {header.isPlaceholder
                        ? null
                        : flexRender(
                            header.column.columnDef.header,
                            header.getContext()
                          )}
                    </Th>
                  ))}
                </Tr>
              ))}
            </Thead>
        <Tbody>
              {table.getRowModel().rows.map((row) => (
                <Tr key={row.id}>
                  {row.getVisibleCells().map((cell) => (
                    <Td key={cell.id}>
                      {flexRender(
                        cell.column.columnDef.cell,
                        cell.getContext()
                      )}
                    </Td>
                  ))}
                </Tr>
              ))}
        </Tbody>
...

安裝選取列功能

要給 table 選取列的功能,要先幫他做一個 state,並且放進去 table 裡面 :

import {
  const [rowSelection, setRowSelection] = useState({});
  const table = useReactTable({
  data,
  columns,
  state: {
    rowSelection, // 這裡
  },
  onRowSelectionChange: setRowSelection,
  getCoreRowModel: getCoreRowModel(), // 這裡
  getSortedRowModel: getSortedRowModel(),
});

將新增 todo 功能交給 react-table

如果沒有接下來的步驟,會發現 react-table 並不會因為 list 的新增而更新表格內容,不過這個解決辦法並不困難。

首先,使用監聽 list,並且在 list 改變之時將它 set 回 data,當 data 改變時,表格才會更新 :

useEffect(() => setData([...list]), [list]);

repo

附上程式碼

結語

使用 react-table 最難的不是安裝功能上去,而是前面的定義與把表格如自己想要的樣子輸出在畫面上...基本上只要能夠做到以上兩件事情,其他的功能都可以讓 react-table 協助完成。

學習的難度是有,但之後功能能夠成功運轉起來,成就感也非凡。

參考資料


上一篇
Day-16 掌控表格的行與列 react-table 基本安裝與使用
下一篇
Day-18 專案演練 - 重構程式碼
系列文
新手前端與真實世界的開發 feat.React 與他的夥伴30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言