[React] React Table で テーブル内のファジー(あいまい)検索 をしてみる | 心を無にして始める React

準備
TanStack Table v8 で React Table を使うのはこちら (*’▽’)
バックエンドには、いつもの json-server を使います。
検索
イメージ

シンプルな検索
検索用の入力フォームに文字を入力されると、検索を実行して結果を表示します。
検索方法には「入力された文字列が含まれているか」を採用します。
また、検索用の入力フォームに文字を入力して 0.5秒間 変化がなければ、検索を実行するようにします。
(入力中はすぐに検索せず、待機させます。)
今回は Table.js を編集していきます。
import React, { useEffect, useState } from 'react';
import { Table as BootstrapTable } from 'react-bootstrap';
import { useReactTable, flexRender, getCoreRowModel, getFilteredRowModel, getSortedRowModel } from '@tanstack/react-table';
const filter = (row, columnId, value) => {
  return String(row.getValue(columnId)).indexOf(value) !== -1;
}
const DebouncedInput = ({
  value: initialValue,
  onChange,
  debounce = 500,
  ...props
}) => {
  const [value, setValue] = useState(initialValue)
  useEffect(() => {
    setValue(initialValue)
  }, [initialValue])
  useEffect(() => {
    const timeout = setTimeout(() => onChange(value), debounce);
    return () => clearTimeout(timeout)
  }, [value])
  return (
    <input {...props} value={value} onChange={e => setValue(e.target.value)} />
  )
}
const Table = React.forwardRef(({
  columns,
  rows,
  ...otherProps
}, ref) => {
  const [globalFilter, setGlobalFilter] = useState('')
  const table = useReactTable({
    columns,
    data: rows,
    state: {
      globalFilter,
    },
    onGlobalFilterChange: setGlobalFilter,
    globalFilterFn: filter,
    getCoreRowModel: getCoreRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getSortedRowModel: getSortedRowModel(),
  })
  return (
    <div className="w-100">
      <div className="mb-2 w-100" style={{ paddingLeft: 1, paddingRight: 1 }}>
        <DebouncedInput
          value={globalFilter ?? ''}
          onChange={value => setGlobalFilter(String(value))}
          className="w-100 p-2 font-lg shadow border border-block"
          placeholder="検索"
        />
      </div>
      <BootstrapTable ref={ref} {...otherProps}>
        <thead>
          {
            table.getHeaderGroups().map(headerGroup => (
              <tr key={headerGroup.id}>
                {
                  headerGroup.headers.map(header => {
                    return (
                      <th key={header.id}>
                        {
                          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>
      </BootstrapTable>
    </div>
  )
})
export default Table;App.js は 前の記事 と同じです。
少しだけカスタマイズ
さすがに、このままだと使えないので、ある程度使える形にします (*’▽’)
- スペース区切りは OR 条件として検索したい
- 大文字小文字は区別したくない
filter 関数を編集します (/・ω・)/
const filter = (row, columnId, value) => {
  const values = value.split(/\s+/).map(x => x.toLowerCase());
  const cellText = String(row.getValue(columnId)).toLowerCase();
  
  return values.some(value => cellText.indexOf(value) !== -1);
}スペース区切りを OR条件
ファジーな検索
検索方法を「あいまい検索」に変えます。
準備
追加でインストールするものがあります。
npm install @tanstack/match-sorter-utils
局所的なサンプル
filter 用の関数をファジー検索に対応した形にします。
import { rankItem } from '@tanstack/match-sorter-utils';
const fuzzyFilter = (row, columnId, value, addMeta) => {
  const itemRank = rankItem(row.getValue(columnId), value);
  addMeta({
    itemRank,
  })
  return itemRank.passed;
}ぜんたい
シンプルな検索の Table.js をベースに編集します。
import React, { useEffect, useState } from 'react';
import { Table as BootstrapTable } from 'react-bootstrap';
import { useReactTable, flexRender, getCoreRowModel, getFilteredRowModel, getSortedRowModel } from '@tanstack/react-table';
import { rankItem } from '@tanstack/match-sorter-utils';
const fuzzyFilter = (row, columnId, value, addMeta) => {
  const itemRank = rankItem(row.getValue(columnId), value);
  addMeta({
    itemRank,
  })
  return itemRank.passed;
}
const DebouncedInput = ({
  value: initialValue,
  onChange,
  debounce = 500,
  ...props
}) => {
  const [value, setValue] = useState(initialValue)
  useEffect(() => {
    setValue(initialValue)
  }, [initialValue])
  useEffect(() => {
    const timeout = setTimeout(() => onChange(value), debounce);
    return () => clearTimeout(timeout)
  }, [value])
  return (
    <input {...props} value={value} onChange={e => setValue(e.target.value)} />
  )
}
const Table = React.forwardRef(({
  columns,
  rows,
  ...otherProps
}, ref) => {
  const [globalFilter, setGlobalFilter] = useState('')
  const table = useReactTable({
    columns,
    data: rows,
    state: {
      globalFilter,
    },
    onGlobalFilterChange: setGlobalFilter,
    globalFilterFn: fuzzyFilter,
    getCoreRowModel: getCoreRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getSortedRowModel: getSortedRowModel(),
    debugTable: true,
  })
  return (
    <div className="w-100">
      <div className="mb-2 w-100" style={{ paddingLeft: 1, paddingRight: 1 }}>
        <DebouncedInput
          value={globalFilter ?? ''}
          onChange={value => setGlobalFilter(String(value))}
          className="w-100 p-2 font-lg shadow border border-block"
          placeholder="検索"
        />
      </div>
      <BootstrapTable ref={ref} {...otherProps}>
        <thead>
          {
            table.getHeaderGroups().map(headerGroup => (
              <tr key={headerGroup.id}>
                {
                  headerGroup.headers.map(header => {
                    return (
                      <th key={header.id}>
                        {
                          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>
      </BootstrapTable>
    </div>
  )
})
export default Table;はい、できました。

![[React] TanStack Table v8 を React で使ってみる | 心を無にして始める React](https://neko-note.org/wp-content/uploads/tanstack-table-thumbnail-100x100.png)
 https://neko-note.org/react-tanstack-table-v8/976
 https://neko-note.org/react-tanstack-table-v8/976![[React] Axios で通信してみる | 心を無にして始める React](https://neko-note.org/wp-content/uploads/react-axios-thumbnail-100x100.png)














