React open 从 react-table 弹出

React open pop up from react-table

我正在使用 react.js 构建一个应用程序,它有一个使用 react table library.

构建的 table

在这个 table 中,我想要一个按钮(或其他按钮)来打开一个显示 png 的弹出窗口。 png 由单元格内容决定,因此在所有弹出窗口中可能相同也可能不同。

看来我需要使用一个非常简单的模式,所以我决定使用 react bootstrap library 构建它,如下所示

import React, { useState } from "react";
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';

const PopUp = ({ link }) => {
  const [show, setShow] = useState(false);

  const handleClose = () => setShow(false);
  const handleShow = () => setShow(true);

  return (
    <>
      <Button variant="primary" onClick={handleShow} size="sm">
        Pathway
      </Button>

      <Modal
        show={show}
        onHide={handleClose}
        dialogClassName={'IMG_path'}
        size="lg"
      >
        <Modal.Header closeButton>
          <Modal.Title>IMG title</Modal.Title>
        </Modal.Header>
        <Modal.Body closeButton>
          <img src={"./img/" + link}
               id="id"
               alt="description"
               className="img-fluid"/>
        </Modal.Body>
      </Modal>
    </>
  );
};

export default PopUp;

然后我在反应 table 旁边测试了它,我的意思是在同一个 div 内,它工作得很好。

然后我使用下面的代码

添加到table
import React from "react";
import { useTable, usePagination, useSortBy } from 'react-table';

import PopUp from "./PopUp";

const MakePopup = ({ values }) =>  values && <PopUp link={values} /> ;

function Table({ columns, data }) {
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    prepareRow,
    page, 
    canPreviousPage,
    canNextPage,
    pageOptions,
    pageCount,
    gotoPage,
    nextPage,
    previousPage,
    setPageSize,
    state: { pageIndex, pageSize },
  } = useTable(
    {
      columns,
      data,
      autoResetPage: true,
      initialState: { pageIndex: 0 },
    },
    // hook for sorting
    useSortBy,
    // hook for pagination
    usePagination
  )

  return (
    <>
      <table {...getTableProps()} className={"table table-striped table-bordered table-hover"}>
        <thead>
          {headerGroups.map(headerGroup => (
            <tr {...headerGroup.getHeaderGroupProps()}>
              {headerGroup.headers.map(column => (
                // Add the sorting props to control sorting. For this example
                // we can add them into the header props
                <th {...column.getHeaderProps(column.getSortByToggleProps())} scope={"col"}>
                  {column.render('Header')}
                  <span>
                    {column.isSorted
                      ? column.isSortedDesc
                        ? ' '
                        : ' '
                      : ''}
                  </span>
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody {...getTableBodyProps()}>
          {page.map((row, i) => {
            prepareRow(row)
            return (
              <tr {...row.getRowProps()}>
                {row.cells.map(cell => {
                  return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
                })}
              </tr>
            )
          })}
        </tbody>
      </table>
      {/*
        Pagination can be built however you'd like.
        This is just a very basic UI implementation:
      */}
      <div className="pagination">
        <select
          value={pageSize}
          onChange={e => {
            setPageSize(Number(e.target.value))
          }}
        >
          {[10, 20, 30, 40, 50].map(pageSize => (
            <option key={pageSize} value={pageSize}>
              Show {pageSize}
            </option>
          ))}
        </select>{' '}
        <button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
          {'<<'}
        </button>{' '}
        <button onClick={() => previousPage()} disabled={!canPreviousPage}>
          {'<'}
        </button>{' '}
        <span className='page-of'>
          Page{' '}
          <strong>
            {pageIndex + 1} of {pageOptions.length}
          </strong>{' '}
        </span>
        <button onClick={() => nextPage()} disabled={!canNextPage}>
          {'>'}
        </button>{' '}
        <button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
          {'>>'}
        </button>
      </div>
    </>
  )
}

function MyTable(props) {
  const columns = [
    {
      Header: 'AA',
      accessor: 'AA',
    },
    {
      Header: 'BB',
      accessor: 'BB',
      sortType: "basic"
    },
    ...
    ...
    ...
    {
      Header: 'Img',
      accessor: 'url',
      sortType: "basic",
      disableSortBy: true,
      Cell: ({ cell: {value} }) => <MakePopup values={value} />
    }
  ];

  const data = props.tableData;

  return (
      <Table columns={columns} data={data} />
  )
}

现在单元格具有调用模式的按钮,但它从未出现:(

在这里搜索我发现 and 答案表明模式应该在 table 之外而不是每个单元格中的下一个按钮所以我将代码修改为这个

import React, { useState } from "react";
import { useTable, usePagination, useSortBy } from 'react-table';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';

// PopUp 
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const [link, setLink] = useState('.png');

const handleClick = ( link ) => {
  setShow(true);
  setLink(link);
};

function Table({ columns, data }) {

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    prepareRow,
    page, 
    canPreviousPage,
    canNextPage,
    pageOptions,
    pageCount,
    gotoPage,
    nextPage,
    previousPage,
    setPageSize,
    state: { pageIndex, pageSize },
  } = useTable(
    {
      columns,
      data,
      autoResetPage: true,
      initialState: { pageIndex: 0 },
    },
    // hook for sorting
    useSortBy,
    // hook for pagination
    usePagination
  )

  return (
    <>
      <table {...getTableProps()} className={"table table-striped table-bordered table-hover"}>
        <thead>
          {headerGroups.map(headerGroup => (
            <tr {...headerGroup.getHeaderGroupProps()}>
              {headerGroup.headers.map(column => (
                // Add the sorting props to control sorting. For this example
                // we can add them into the header props
                <th {...column.getHeaderProps(column.getSortByToggleProps())} scope={"col"}>
                  {column.render('Header')}
                  {/* Add a sort direction indicator */}
                  <span>
                    {column.isSorted
                      ? column.isSortedDesc
                        ? ' '
                        : ' '
                      : ''}
                  </span>
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody {...getTableBodyProps()}>
          {page.map((row, i) => {
            prepareRow(row)
            return (
              <tr {...row.getRowProps()}>
                {row.cells.map(cell => {
                  return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
                })}
              </tr>
            )
          })}
        </tbody>
      </table>
      {/*
        Pagination can be built however you'd like.
        This is just a very basic UI implementation:
      */}
      <div className="pagination">
        <select
          value={pageSize}
          onChange={e => {
            setPageSize(Number(e.target.value))
          }}
        >
          {[10, 20, 30, 40, 50].map(pageSize => (
            <option key={pageSize} value={pageSize}>
              Show {pageSize}
            </option>
          ))}
        </select>{' '}
        <button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
          {'<<'}
        </button>{' '}
        <button onClick={() => previousPage()} disabled={!canPreviousPage}>
          {'<'}
        </button>{' '}
        <span className='page-of'>
          Page{' '}
          <strong>
            {pageIndex + 1} of {pageOptions.length}
          </strong>{' '}
        </span>
        <button onClick={() => nextPage()} disabled={!canNextPage}>
          {'>'}
        </button>{' '}
        <button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
          {'>>'}
        </button>
      </div>
    </>
  )
}

function MyTable(props) {
  const columns = [
    {
      Header: 'AA',
      accessor: 'AA',
    },
    {
      Header: 'BB',
      accessor: 'BB',
      sortType: "basic"
    },
    ...
    ...
    ...
    {
      Header: 'Img',
      accessor: 'url',
      sortType: "basic",
      disableSortBy: true,
      Cell: ({ cell: { png } }) => png && (
        <Button
          variant="primary"
          onClick={ () => handleClick(png) }
          size="sm"
        >
          Pathway
        </Button>
      )
    }
  ];

  const data = props.genesData;

  return (
    <>
      <Table columns={columns} data={data} />
      <Modal
        show={show}
        onHide={handleClose}
        dialogClassName={'IMG_path'}
        size="lg"
      >
        <Modal.Header closeButton>
          <Modal.Title>IMG title</Modal.Title>
        </Modal.Header>
        <Modal.Body closeButton>
          <img src={"./img/" + link}
               id="id"
               alt="description"
               className="img-fluid"/>
        </Modal.Body>
      </Modal>
    </>
  )
}

export default MyTable;

现在出现以下编译错误,应用程序无法启动

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app See react-invalid-hook-call for tips about how to debug and fix this problem.

指向这部分代码

   4 | import Button from 'react-bootstrap/Button';
   5 | 
   6 | // PopUp states
>  7 | const [show, setShow] = useState(false);
   8 | const handleClose = () => setShow(false);
   9 | // const handleShow = () => setShow(true);
  10 | const [link, setLink] = useState('.png');

现在我不确定如何解决这个问题。我一直在考虑使用 redux 之类的状态管理库,但我认为它不能解决问题。我正在使用来自 bootstrap 的模态,但我会使用任何它是什么,因为我认为模态对于我需要的功能来说可能有点太多了。

谢谢

=========================

根据答案编辑

按照@Afzal Zubair 的建议,我将钩子移动到函数内部,如下所示:

import React, { useState } from "react";
import { useTable, usePagination, useSortBy } from 'react-table';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';

function Table({ columns, data }) {

  // PopUp states
  const [show, setShow] = useState(false);
  const handleClose = () => setShow(false);
  // const handleShow = () => setShow(true);
  const [link, setLink] = useState('.png');

  const handleClick = ( link ) => {
    setShow(true);
    setLink(link);
  };

  const {
    getTableProps,
    getTableBodyProps,
  ...
  ...
  ...
  ...
  return (
    <>
      <Table columns={columns} data={data} />
      <Modal
        show={show}
        onHide={handleClose}
        dialogClassName={'IMG_path'}
        size="lg"
      >
        <Modal.Header closeButton>
          <Modal.Title>KEGG pathway</Modal.Title>
        </Modal.Header>
        <Modal.Body closeButton>
          <img src={"./img/" + link}
               alt="plant-pathogen_interaction"
               className="img-fluid"/>
        </Modal.Body>
      </Modal>
    </>
  )
}

现在我得到一个不同的编译错误

编译失败

./src/components/table/ReactTable_PopUp.jsx Line 193:27: 'handleClick' is not defined no-undef Line 208:15: 'show' is not defined no-undef Line 209:17: 'handleClose' is not defined no-undef Line 217:32: 'link' is not defined no-undef

Search for the keywords to learn more about each error.

参考以下代码

 190     Cell: ({ cell: { png } }) => png && (
 191       <Button
 192         variant="primary"
 193         onClick={ () => handleClick(png) }
 194         size="sm"
 195       >
 196         Pathway
 197       </Button>
 198     )

 204  return (
 205    <>
 206      <Table columns={columns} data={data} />
 207      <Modal
 208        show={show}
 209        onHide={handleClose}

 216        <Modal.Body closeButton>
 217          <img src={"./img/" + link}
 218               alt="plant-pathogen_interaction"
 219               className="img-fluid"/>
 220        </Modal.Body>

谢谢

============================================= =======================

已解决

@Afzal Zubair 是对的,钩子需要移动到 Functional 元素,我代码中的函数 MyTable

function MyTable(props) {

  // PopUp states
  const [show, setShow] = useState(false);
  const handleClose = () => setShow(false);
  const [link, setLink] = useState('.png');

  const handleClick = ( link ) => {
    setShow(true);
    setLink(link);
  };  

  const columns = [
    {
      Header: 'AA',
      accessor: 'AA',
    },
    {
      Header: 'BB',
      accessor: 'BB',
      sortType: "basic"
    },
    ...
    ...

现在可以使用了!

你似乎在 React 组件之外使用了 useState 钩子。

   4 | import Button from 'react-bootstrap/Button';
   5 | 
   6 | // PopUp states
>  7 | const [show, setShow] = useState(false);
   8 | const handleClose = () => setShow(false);
   9 | // const handleShow = () => setShow(true);
  10 | const [link, setLink] = useState('.png');

你应该在 Table 组件之外使用模式,但你不能在反应组件之外使用反应钩子。

我建议在 React 组件中使用这些钩子,准确地说是功能组件。 你可以参考这篇文档更好的理解,link.