Material-UI テーブル内の行を検索でフィルターする

# 初めに

この記事では以下のように検索バーに文字を入力すると、テーブルの行をフィルターするコンポーネントについて書きました。

![2022-05-03_07h32_02.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/b72f365c-65ff-802a-9a8b-9a0ca4a16d3d.gif)

Material-UI（MUI）は React 用のコンポーネントやアイコンなどを提供するライブラリです。

https://mui.com/

# コード

```React:App.js
import { useState } from "react";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Paper from "@mui/material/Paper";
import Search from "./Search";

const createData = (name, calories, fat, carbs, protein) => {
  return { name, calories, fat, carbs, protein };
};

// テーブルの行の初期値
const initialRows = [
  createData("Frozen yoghurt", 159, 6.0, 24, 4.0),
  createData("Ice cream sandwich", 237, 9.0, 37, 4.3),
  createData("Eclair", 262, 16.0, 24, 6.0),
  createData("Cupcake", 305, 3.7, 67, 4.3),
  createData("Gingerbread", 356, 16.0, 49, 3.9)
];

// テーブルを表示するコンポーネント
const BasicTable = (props) => {
  const { rows } = props;
  return (
    <TableContainer component={Paper}>
      <Table sx={{ minWidth: 650 }} aria-label="simple table">
        <TableHead>
          <TableRow>
            <TableCell>Dessert (100g serving)</TableCell>
            <TableCell align="right">Calories</TableCell>
            <TableCell align="right">Fat&nbsp;(g)</TableCell>
            <TableCell align="right">Carbs&nbsp;(g)</TableCell>
            <TableCell align="right">Protein&nbsp;(g)</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {rows.map((row) => (
            <TableRow
              key={row.name}
              sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
            >
              <TableCell component="th" scope="row">
                {row.name}
              </TableCell>
              <TableCell align="right">{row.calories}</TableCell>
              <TableCell align="right">{row.fat}</TableCell>
              <TableCell align="right">{row.carbs}</TableCell>
              <TableCell align="right">{row.protein}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
};

const App = () => {
  // テーブルの行
  const [rows, setRows] = useState([...initialRows]);
  // 検索バーに入力された文字
  const [searched, setSearched] = useState("");

  return (
    <div className="App">
      <Search
        initialRows={initialRows}
        searched={searched}
        setRows={setRows}
        setSearched={setSearched}
      />
      <BasicTable rows={rows} />
    </div>
  );
};

export default App;
```

```React:Search
import SearchIcon from "@mui/icons-material/Search";
import TextField from "@mui/material/TextField";

// 検索バーのコンポーネント
const SearchMovie = (props) => {
  const { searched, initialRows, setRows, setSearched } = props;

  // 検索文字によってテーブルの行をフィルター関数
  const requestSearch = (searchedVal) => {
    const filteredRows = initialRows.filter((row) => {
      return row.name.toLowerCase().includes(searchedVal.toLowerCase());
    });
    setRows(filteredRows);
  };

  // 検索バーの文字が変化したときにフィルターを実行する関数
  const changeSearchedHandler = (event) => {
    setSearched(event.target.value);
    requestSearch(event.target.value);
  };

  return (
    <div>
      <SearchIcon />
      <TextField
        id="standard-basic"
        label="Search"
        variant="standard"
        value={searched}
        onChange={(event) => changeSearchedHandler(event)}
      />
    </div>
  );
};

export default SearchMovie;
```

```React:index.js
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);
```

# 説明

テーブルの初期値を設定します。これは検索バーが空文字になったとき、テーブルの全データを表示するために設定しています。

```React
// テーブルの行の初期値
const initialRows = [
  createData("Frozen yoghurt", 159, 6.0, 24, 4.0),
  createData("Ice cream sandwich", 237, 9.0, 37, 4.3),
  createData("Eclair", 262, 16.0, 24, 6.0),
  createData("Cupcake", 305, 3.7, 67, 4.3),
  createData("Gingerbread", 356, 16.0, 49, 3.9)
];
```

テーブルコンポーネントには行の state を渡しています。このようにすることでフィルター後の行のみをテーブルに表示することができます。

```React
// テーブルを表示するコンポーネント
const BasicTable = (props) => {
  const { rows } = props;
  return (
    <TableContainer component={Paper}>
      <Table sx={{ minWidth: 650 }} aria-label="simple table">
        <TableHead>
          <TableRow>
            <TableCell>Dessert (100g serving)</TableCell>
            <TableCell align="right">Calories</TableCell>
            <TableCell align="right">Fat&nbsp;(g)</TableCell>
            <TableCell align="right">Carbs&nbsp;(g)</TableCell>
            <TableCell align="right">Protein&nbsp;(g)</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {rows.map((row) => (
            <TableRow
              key={row.name}
              sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
            >
              <TableCell component="th" scope="row">
                {row.name}
              </TableCell>
              <TableCell align="right">{row.calories}</TableCell>
              <TableCell align="right">{row.fat}</TableCell>
              <TableCell align="right">{row.carbs}</TableCell>
              <TableCell align="right">{row.protein}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
};
```

`App` コンポーネントでテーブルの行と検索バーの文字の state を管理しています。それを子コンポーネントである `Search` コンポーネント（検索バーの操作やフィルターを行うコンポーネント）と `BasicTable` （テーブルを表示するコンポーネント）に渡します。

```React
const App = () => {
  // テーブルの行
  const [rows, setRows] = useState([...initialRows]);
  // 検索バーに入力された文字
  const [searched, setSearched] = useState("");

  return (
    <div className="App">
      <Search
        initialRows={initialRows}
        searched={searched}
        setRows={setRows}
        setSearched={setSearched}
      />
      <BasicTable rows={rows} />
    </div>
  );
};
```

`searchedVal` という引数で検索バーの文字を受け取り、その文字でテーブルの `name` 列をフィルターします。

```React
  // 検索文字によってテーブルの行をフィルター関数
  const requestSearch = (searchedVal) => {
    const filteredRows = initialRows.filter((row) => {
      return row.name.toLowerCase().includes(searchedVal.toLowerCase());
    });
    setRows(filteredRows);
  };
```

検索バーの文字が変化したときに、親コンポーネントの `searched` という state と、フィルターを実行します。

```React
  // 検索バーの文字が変化したときにフィルターを実行する関数
  const changeSearchedHandler = (event) => {
    setSearched(event.target.value);
    requestSearch(event.target.value);
  };
```

# 参考記事

https://mui.com/