使用带有graphql的react hooks时,按钮元素中的onClick不会触发react-table中的重新渲染

Posted

技术标签:

【中文标题】使用带有graphql的react hooks时,按钮元素中的onClick不会触发react-table中的重新渲染【英文标题】:onClick in button element not triggering re-render in react-table when using react hooks with graphql 【发布时间】:2020-02-24 05:33:54 【问题描述】:

我正在尝试使用 react-table 呈现一个表,但是,该表具有从 GraphQL 数据库中提取的不同状态。每个按钮都应该有效地为表格呈现相同的 UI,但只显示与用户单击的按钮相关联的正确状态的货件。

我的货件查询如下:

import  gql  from 'apollo-boost';

export const GET_SHIPMENTS = gql`
      
        shipments 
          created_at
          id
          status
          orders 
            order_items
          
        
      
    `;

我使用GET_SHIPMENTS 查询的表格组件如下所示:

import React,  useState  from "react";
import  graphql  from 'react-apollo';
import  GET_SHIPMENTS  from '../graphql/ShipmentQueries';
import ReactTable from 'react-table';

import 
  Card,
  CardBody,
  Row,
  ButtonGroup,
  Button
 from "reactstrap";

function OrderTable ( loading, shipments ) 
  const [shownShipment, setShownShipment] = useState(status: "created");

  const columns = [
    
      Header: 'ID',
      accessor: 'id',
    ,
    
      Header: 'Status',
      accessor: 'status',
    ,
    
      Header: 'Item Count',
      accessor: 'orders[0].order_items'
    ,
    
      Header: 'Time Stamp',
      accessor: 'created_at',
    ,
  ];

if (loading) return <p>Loading...</p>;
    return (
        <div className="content">
            <ButtonGroup className="center">
                <Button 
                  name="created" 
                  onClick=() => setShownShipment(shownShipment.status === "created")
                >
                  Created
                </Button>

                <Button 
                  name="awaiting driver" 
                  onClick=() => setShownShipment(shownShipment.status === "awaiting_driver")
                >
                  Awaiting Driver
                </Button>

                <Button 
                  name="delivered" 
                  onClick=() => setShownShipment(shownShipment.status === "delivered")
                >
                  Delivered
                </Button>
            </ButtonGroup>
          <Row className="mt-5">
              <Card>
                <CardBody>
                  <ReactTable
                    data=shipments
                    columns=columns
                    sortable=true
                    resizable=false
                    minRows=10
                  />
                </CardBody>
              </Card>
          </Row>
        </div>
    );
  

  export const OrderTableWithData = graphql(GET_SHIPMENTS, 
    props: (data:  loading, shipments, shownShipments ) => (
      loading,
      shipments,
      shownShipments,
    ),
  )(OrderTable);

这是我第一次介绍使用钩子,所以我知道我可能没有正确使用它们。我不确定是否必须使用 useEffect 钩子。我已经搜索了 Hooks 文档,但似乎找不到明确的答案。我觉得useState 应该可以工作。我是否必须重新渲染整个 ReactTable 元素?

【问题讨论】:

当您以这种方式在按钮按下时调用 setShownShipment 时,您将根据当前状态将 shownShipment 设置为 true 或 false。所以,它从“创造”开始,然后变成真或假。这是你的意图吗?我认为您可能正在尝试在交付的按钮按下案例中执行类似 'setShownShipment(status: 'delivered')' 的操作。 我不一定想说状态是真还是假。我想更改状态,从而更新表格的状态,以表明,如果他们单击Delivered 按钮,状态应更新为delivered,然后渲染表格仅显示已交付的货物。也许在我的钩子里,我应该留下一个空字符串?因为如果我将初始状态设置为 created 然后在我的按钮内,我想我必须做第三个? 您当前运行的 buttonClick 代码仅将显示状态设置为 true 或 false,因为 (shownShipment.status === "created") 仅解析为 true 或 false。请参阅下面的答案。 【参考方案1】:

如果您希望showsShipment.status 为与交货状态对应的字符串(例如'delivered'、'awaiting_driver')等,您的buttonClick 代码应如下所示:

onClick=() => setShownShipment(status: "delivered") 

尝试像这样向 react 组件添加一个效果,以查看单击每个按钮后的状态更新:

useEffect(() =>  console.log(shownShipment); , [shownShipment]);

现在您已将showShipment 设置为所需状态,您可以根据此过滤来自GQL 的总发货清单。 UseState 再次获取您将实际提供给您的餐桌的货物清单。 useEffect 在这里也会有所帮助。像这样的:

// at the top of the component
var [shipmentsToDisplay, setShipmentsToDisplay] = useState([]);

useEffect(() => 
    // loop through all shipments and create a new array for only the ones 
    // you want to show.
    const filteredShipments = 
        shipments && 
            shipments.map(shipment => 
                if (shipment.status === shownShipment) 
                    return shipment;
                
            

    setShipmentsToDisplay(filteredShipments);
, [shownShipment, shipments]);


// Render your table with these shipments rather than props.shipments
<ReactTable
    data=shipmentsToDisplay
    columns=columns
    sortable=true
    resizable=false
    minRows=10       
/>

【讨论】:

所以我认为这可能是第一步,但是,当我实际单击按钮时,表格不会过滤为 显示那些 交付或你有什么。我想我应该更具体地说明我想要按钮做什么。我最初有一个 filterShipments 函数,但不知道如何将它传递给 onClick 处理程序。 在控制台中,我得到了与状态关联的对象,所以看起来是正确的。 检查我的编辑,我想这就是你要找的。​​span> 我认为这有利于过滤,但由于某种原因,我在setShipmentsToDisplay(filteredShipments) var setShipmentsToDisplay: (value: React.SetStateAction&lt;any[]&gt;) =&gt; void Parsing error: Unexpected token, expected "," 24 | 25 | &gt; 26 | setShipmentsToDisplay(filteredShipments); | ^ 27 | , [shownShipment, shipments]); 28 | 29 |eslint ',' expected.ts(1005) 下收到此错误 所以由于某种原因这仍然无法正常工作,不知道从哪里开始。我必须注释掉它才能显示数据,以便我可以处理其他一些事情。【参考方案2】:

此代码的解决方案如下: (为简洁起见,删除了一些代码)

function OrderTable ( loading, shipments ) 
  const [shipmentsToDisplay, setShipmentsToDisplay] = useState([]);

  useEffect(() => 
    if(shipments) 
      filterShipments("created");
    
  , [shipments]);

  function filterShipments(status) 
    let shownShipments = [];
    shipments.forEach(element => 
      if(element.status === status) 
        shownShipments.push(element);
      
    );
    setShipmentsToDisplay(shownShipments);
  

if (loading) return <Loading />;
    return (
        <div className="content">
            <ButtonGroup className="center">
                <Button name="pending" onClick=() => filterShipments("created")>
                  Pending
                </Button>
                <Button name="ready" onClick=() => filterShipments("awaiting_driver")>
                  Ready
                </Button>
                <Button name="completed" onClick=() => filterShipments("delivered")>
                  Completed
                </Button>
            </ButtonGroup>
          <Row className="mt-5">
            <ReactTable
              data=shipmentsToDisplay
              columns=columns
              sortable=true
              resizable=false
              defaultPageSize=5
              className="-striped -highlight"
              getTrProps=getTrProps
              SubComponent=row => 
                return (
                  <ShipmentDetails
                    shipments=shipments
                  />
                )
              
            />
          </Row>
        </div>
    );
  

  export const OrderTableWithData = graphql(GET_SHIPMENTS, 
    props: (data:  loading, shipments ) => (
      loading,
      shipments
    ),
  )(OrderTable);

解决方案是登陆Pending 货件,因此对于我的状态,我使用useEffect 钩子登陆以created 作为状态的货件。

对于我的 onClick 函数,我决定从 created 发货开始过滤每个发货,并根据与按钮单击相关联的状态更新状态。我不知道这是否有意义。我是 hooks 和 GraphQL 的新手。

【讨论】:

以上是关于使用带有graphql的react hooks时,按钮元素中的onClick不会触发react-table中的重新渲染的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 React-Hooks 和 Apollo Client 在 React-GraphQL 中实现搜索功能?

使用 React Hook 动态设置 GraphQL 变量不起作用

找不到模块“graphql-hooks”或其相应的类型声明

GraphQL Apollo React Hooks 查询根据初始化顺序返回 null

使用 Hooks 在 React 中对 Apollo Graphql 进行单元测试

有没有办法使用 @apollo/react-hooks 访问多个 graphql 突变的选项