WEB/REACT

[REACT] react-table row selection/ column hidding

자바칩 프라푸치노 2021. 12. 17. 18:43

저번에는 MUI-DATA GRID를 사용해서 테이블을 만들어보았다.

mui에서는 column hide, select , 등등 아주 쉽게 제공해주어서 개발하는데 정말 얼마 걸리지 않는다.

그런데 react-table을 사용하면 이것들을 하나하나 추가해주어야한다.

이번에는 react-table을 사용해보자

(개인적으로 data grid가 코드가 깔끔하고 아주 빠르게 개발할 수 있어서 더 좋다. )

 

 

[Row Selection]

참고: column과 rowData는 다른 폴더에 만들었음

export const reactTableColumn = [
  {
    Header: "오더등록일",
    accessor: "orderDate",
  },
...
];
export const rows = [
  {
    id: "1",
    orderDate: "2021-12-03",
...
  },
 
];

 

import React, { useEffect, useMemo, useState } from "react";
import { useTable, useRowSelect } from "react-table";
import { data } from "../data/data";
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";
import { reactTableColumn } from "../data/reactTableColumn";
import { rows } from "../data/rows";
import { Box, Button, Stack } from "@mui/material";

const tableStyle = css`
  border-spacing: 0;
  border: 1px solid black;
  tr {
    :last-child {
      td {
        border-bottom: 0;
      }
    }
  }

  th,
  td {
    margin: 0;
    padding: 0.5rem;
    border-bottom: 1px solid black;
    border-right: 1px solid black;

    :last-child {
      border-right: 0;
    }
  }
`;

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

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

    const [isTrue, setIsTrue] = useState(false);
    // const isDisabled = row.orginal.orderAmount > 1;

    useEffect(() => {
      if (row?.values?.orderAmount < 2) {
        setIsTrue(true);
      }
    }, [row]);
    return (
      <>
        <input type="checkbox" ref={resolvedRef} {...rest} disabled={isTrue} />
      </>
    );
  }
);

function Table({ columns, data }) {
  // Use the state and functions returned from useTable to build your UI
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    selectedFlatRows,
    state: { selectedRowIds },
  } = useTable(
    {
      columns,
      data,
    },
    useRowSelect,
    (hooks) => {
      hooks.visibleColumns.push((columns) => [
        // Let's make a column for selection
        {
          id: "selection",
          // The header can use the table's getToggleAllRowsSelectedProps method
          // to render a checkbox
          Header: ({ getToggleAllRowsSelectedProps }) => (
            <div>
              <IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
            </div>
          ),
          // The cell can use the individual row's getToggleRowSelectedProps method
          // to the render a checkbox
          Cell: ({ row }) => (
            <div>
              <IndeterminateCheckbox
                {...row.getToggleRowSelectedProps()}
                row={row}
              />
            </div>
          ),
        },
        ...columns,
      ]);
    }
  );

  console.log(selectedFlatRows);

  // Render the UI for your table
  return (
    <>
      <table {...getTableProps()} css={tableStyle}>
        <thead>
          {headerGroups.map((headerGroup) => (
            <tr {...headerGroup.getHeaderGroupProps()}>
              {headerGroup.headers.map((column) => (
                <th {...column.getHeaderProps()}>{column.render("Header")}</th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody {...getTableBodyProps()}>
          {rows.slice(0, 10).map((row, i) => {
            prepareRow(row);
            return (
              <tr {...row.getRowProps()}>
                {row.cells.map((cell) => {
                  return (
                    <td {...cell.getCellProps()}>{cell.render("Cell")}</td>
                  );
                })}
              </tr>
            );
          })}
        </tbody>
      </table>
      <p>Selected Rows: {Object.keys(selectedRowIds).length}</p>
    </>
  );
}
const Receiving = () => {
  return (
    <div>
      <h2>react table 사용</h2>
      <Stack spacing={1} direction="row">
        <Button variant="contained">사입요청하기</Button>
        <Button variant="contained">사입요청하기</Button>
        <Button variant="contained">사입요청하기</Button>
      </Stack>
      <Table columns={reactTableColumn} data={rows} />
    </div>
  );
};

export default Receiving;

참고 : react-table

 

 

react-table 공식 example에서 react forwardRef를 사용해서 checkbox를 구현한다.

여기에 더해서 disabled를 주고 싶어서 props로 row정보를 받아서 어떤 조건일때 checkbox의 disabled를 정해주었다.

 

 

그런데 문제가 하나만 checkbox disabled는 가능한데

전체 버튼을 체크하면 전체가 다 체크가 되어서 밑에 selected Rows가 4가 나온다.

눈으로 보기에는 2개인데.....

 

 

 


 

 

[column hidding]

참고 : react-table

import React, { useEffect, useMemo, useState } from "react";
import { useTable, useRowSelect } from "react-table";
import { data } from "../data/data";
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";
import { reactTableColumn } from "../data/reactTableColumn";
import { rows } from "../data/rows";
import { Box, Button, Stack } from "@mui/material";

const tableStyle = css`
  border-spacing: 0;
  border: 1px solid black;
  tr {
    :last-child {
      td {
        border-bottom: 0;
      }
    }
  }

  th,
  td {
    margin: 0;
    padding: 0.5rem;
    border-bottom: 1px solid black;
    border-right: 1px solid black;

    :last-child {
      border-right: 0;
    }
  }
`;

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

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

    const [isTrue, setIsTrue] = useState(false);
    // const isDisabled = row.orginal.orderAmount > 1;

    useEffect(() => {
      if (row?.values?.orderAmount < 2) {
        setIsTrue(true);
      }
    }, [row]);
    return (
      <>
        <input type="checkbox" ref={resolvedRef} {...rest} disabled={isTrue} />
      </>
    );
  }
);

function Table({ columns, data }) {
  // Use the state and functions returned from useTable to build your UI
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    selectedFlatRows,
    allColumns,
    getToggleHideAllColumnsProps,
    state: { selectedRowIds },
  } = useTable(
    {
      columns,
      data,
    },
    useRowSelect,
    (hooks) => {
      hooks.visibleColumns.push((columns) => [
        // Let's make a column for selection
        {
          id: "selection",
          // The header can use the table's getToggleAllRowsSelectedProps method
          // to render a checkbox
          Header: ({ getToggleAllRowsSelectedProps }) => (
            <div>
              <IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
            </div>
          ),
          // The cell can use the individual row's getToggleRowSelectedProps method
          // to the render a checkbox
          Cell: ({ row }) => (
            <div>
              <IndeterminateCheckbox
                {...row.getToggleRowSelectedProps()}
                row={row}
              />
            </div>
          ),
        },
        ...columns,
      ]);
    }
  );

  console.log(selectedFlatRows);

  // Render the UI for your table
  return (
    <>
      <div>
        <div>
          <IndeterminateCheckbox {...getToggleHideAllColumnsProps()} /> Toggle
          All
        </div>
        {allColumns.map((column) => (
          <div key={column.id}>
            <label>
              <input type="checkbox" {...column.getToggleHiddenProps()} />{" "}
              {column.id}
            </label>
          </div>
        ))}
        <br />
      </div>
      <table {...getTableProps()} css={tableStyle}>
        <thead>
          {headerGroups.map((headerGroup) => (
            <tr {...headerGroup.getHeaderGroupProps()}>
              {headerGroup.headers.map((column) => (
                <th {...column.getHeaderProps()}>{column.render("Header")}</th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody {...getTableBodyProps()}>
          {rows.slice(0, 10).map((row, i) => {
            prepareRow(row);
            return (
              <tr {...row.getRowProps()}>
                {row.cells.map((cell) => {
                  return (
                    <td {...cell.getCellProps()}>{cell.render("Cell")}</td>
                  );
                })}
              </tr>
            );
          })}
        </tbody>
      </table>
      <p>Selected Rows: {Object.keys(selectedRowIds).length}</p>
    </>
  );
}
const Receiving = () => {
  return (
    <div>
      <h2>react table 사용</h2>
      <Stack spacing={1} direction="row">
        <Button variant="contained">사입요청하기</Button>
        <Button variant="contained">사입요청하기</Button>
        <Button variant="contained">사입요청하기</Button>
      </Stack>
      <Table columns={reactTableColumn} data={rows} />
    </div>
  );
};

export default Receiving;

 

 

 

위에 useTable에서 

 allColumns,
 getToggleHideAllColumnsProps,

이거 두개를 추가하면 된다.

   <div>
     <div>
       <IndeterminateCheckbox {...getToggleHideAllColumnsProps()} /> Toggle
       All
     </div>
     {allColumns.map((column) => (
       <div key={column.id}>
         <label>
           <input type="checkbox" {...column.getToggleHiddenProps()} />{" "}
           {column.id}
         </label>
       </div>
     ))}
     <br />
   </div>

table위에 이 코드를 붙인다.

getToggleHiddenProps 이 props는 react-table에 있는 column properties중에 하나인데

이함수는 input checkbox에 들어가는 모든 필요한 props들을 검색하는데 사용되고 이것은 column의 보이고 안보이고를 control할 수 있다 .

 

 

그러면 이렇게 체크하지 않은 데이터는 보여주지 않는다.

data grid에서는 컬럼에 일일이 hide하는 옵션이 붙어있었는데 여기는 컬럼 바깥에 check를 할 수 있다.

둘다 상황에 따라 편한게 다를 수 있다.

이 컨셉으로 가면 원하는 컬럼을 한번에 체크체크해서 테이블을 만들 수 있는 장점이 있는 반면 약간 ui가 깔끔하지 않은 단점이 있을 것 같고, data grid처럼 컬럼마다 hide할 수 있는 속성이 있다면 일일이 체크해줘야하는 불편함이 있지만 ui가 깔끔하다는 장점이 있다.

 

select가 모두 되는 것에 대한 문제는 다음 시간에..

 

728x90