LoginSignup
5
4

More than 1 year has passed since last update.

React Table を TypeScript で使う

Posted at

Tableでちょっと複雑なことをやりたかった

テーブルの中にボタンを入れた上でソートしたり、チェックボックス付けたり。
Screen Shot 2022-02-15 at 22.35.11.png
こんなようなものを作りたかった。意外と大変だった。

今回使ったのは、React Table。
これを Next.js + TypeScript に組み込みたい。

サンプルコードが豊富なので、説明が足らないような気もするが、やりたいことはだいたい何でもできる。Sugeee! が、TypeScript と相性がいまいちよろしくない。map した時に key がないので Warning が出まくる。など、思いの外使いにくかった。

最終的なコードはこちら。
kurab/next-typescript-react-table

準備

yarn create next-app next-typescript-react-table --typescript
cd next-typescript-react-table
yarn add react-table
yarn add @types/react-table --dev

構成の概要

├── components
│   ├── primaryButton.tsx
│   ├── SortableTable.tsx
│   └── SortableTableWithRowSelect.tsx
├── pages
│   ├── _app.tsx
│   └── index.tsx
├── types
│   ├── DataType.ts
│   ├── react-table.d.ts
│   └── react.d.ts
└── package.json

コードを全部書くと長くなるので、要点だけ。

TypeScript 対応

まず、react-table を TypeScript で使うには、型定義をする必要がある。これをしないと、TypeScript で React Table は使えない。

types/react-table.d.ts がそれだが、今回面倒だったので、全ての型定義をこちらからコピペした。

が、使うものだけで良い。今回の場合、Sort と RowSelect 関連のものだけで良い。

Type エラーはまだまだ出るが、全体的な TypeScript 対応はいったんこれだけ。

Sort 可能な Table

基本的にはこのサンプル通りで良い。これを component にしたものが、components/SortableTable.tsx だが、サンプルコードのままだと、key がないので、 Warning が出まくる。

key の設定方法は、以下を参考に作った。

thead の一部だけを抜き取ると、

sample
        <thead>
          {headerGroups.map(headerGroup => (
            <tr {...headerGroup.getHeaderGroupProps()}>
             ...

これがサンプル。key を組み込んだものが

SortableTable.tsx
        <thead>
          {headerGroups.map((headerGroup) => {
            const { key, ...restHeaderGroupProps } =
              headerGroup.getHeaderGroupProps();
            return (
              <tr {...restHeaderGroupProps} key={key}>
               ...

こんな感じで key を作っていく。th の中に span があるが、spankey を与えても Warning は消えないので、th の中身は <></> で囲う。

Table の data として string 以外は渡せるのか?

渡せる。

index.tsx
  const onClickAlert = (name: string) => {
    alert(name);
  };
  const dataButton: Array<DataButton> = useMemo(
    () => [
      {
        col1: 'Hello',
        col2: <PrimaryButton name={'hello'} callback={onClickAlert} />,
      },
      {...},
    ],
    []
  );

こんな感じで、割と何でも渡せる。Type は必要なものを必要なように設定。

Sort 可能なチェックボックス付きの Table

チェックボックス付きはサンプルコードをもとに作る。ソートも付けたいので、html の部分は、前述のコードをそのまま使う。それに加え、今回のサンプルコードで、 useTable を初期化する際に、useSortBy を付けるだけで良い。

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    selectedFlatRows,
    state: { selectedRowIds },
  } = useTable(
    {
      columns,
      data,
    },
    useRowSelect,
    hooks => {
      hooks.visibleColumns.push(columns => [
        {
          id: 'selection',
          Header: ({ getToggleAllRowsSelectedProps }) => (
            <div>
              <IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
            </div>
          ),
          Cell: ({ row }) => (
            <div>
              <IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
            </div>
          ),
        },
        ...columns,
      ])

サンプルコードでやっているのは、hooks で、チェックボックスをくっつけている。
ここで問題が2つ発生する。

まず、これが動かない。

const IndeterminateCheckbox = React.forwardRef(
  ({ indeterminate, ...rest }, ref) => {
    const defaultRef = React.useRef()
    const resolvedRef = ref || defaultRef

    React.useEffect(() => {
      resolvedRef.current.indeterminate = indeterminate
    }, [resolvedRef, indeterminate])

    return (
      <>
        <input type="checkbox" ref={resolvedRef} {...rest} />
      </>
    )
  }
)

解決方法は、こちら。

賢い人がいるもんだ!stackoverflow の方をそのまま使った。

次に、

Cell: ({ row }) => (...)

ここでも Type エラーが出る。Binding element 'any' implicitly has an 'any' type. そのままでも使えるが、build はできない。で、

Cell: ({ row: any }) => (...)

これだと動かない。

こちらを参考に

Cell: ({ row }: { row: any }) => (...)

とすることで、動くようになる。

あとは、コンポーネント間で select したものをやりとりしたいので、親コンポーネントから子コンポーネントに callback を渡して出来上がり。

完成

チェック入れた状態でソート順を変更しても、欲しい値はきちんと取れている。Pagenation とかもやりたいところだが、いまいま必要ないので、必要になった時に。

もともとの要件としては、csv をアップロードして、それを sort したりしながら見て、選んだものだけを DB に登録するという内容だったが、そういうのはエクセルで済ませてからアップロードして欲しいものだ。

おわり。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4