【问题标题】:How can I ensure the refresh of a sub component's props?如何确保子组件的 props 刷新?
【发布时间】:2022-10-15 06:21:21
【问题描述】:

我对 RTK 查询有疑问:我有一个封装表格的第一个组件。当发生刷新时 - 例如当我删除一些数据时 - 父组件接收数据并通过道具将其传递给子组件,但表永远不会更新。所以删除的结果仍然出现在表格中。

生成表刷新的正确“react/redux”方式是什么?

这是一些代码:

const Results = (props) => {


    const router = useRouter()

    const { data, error, isError, isLoading, isFetching } = useGetItemsQuery();

    if(isLoading) {

        return ( 
            <>
                <Spinner animation="border"  size="sm" role="status" />{' '} Please wait while Loading...
            </>
        )
    }
    if(isError) {
        return (
            <>
                <Alert key="warning" variant="warning" style={{marginLeft: "10px"}}>
                        Warning - There was an error with the request :  {error.error}
                </Alert>
            </>
        )
    }


    const sizePerPageList = [
        {
            text: '5',
            value: 5,
        },
    ];

    if (data){

        

        return(
            <>
                <Card>
                    <Card.Body>
                        Traffic results
                        <TableResults data={data} sizePerPageList={sizePerPageList} />
                    </Card.Body>
                </Card>                
            </>
        )   
    }
}

export default Results;

对于带有表格的第二个组件:



const TableResults = (props) => {

  
    console.log('props - data ', props.data);

    const data = React.useMemo(
        () => props.data,
        []
      )

    const columns = React.useMemo(
        () => [
            {
                Header: 'bla bla',
                accessor: 'blabla', 
            }
           
        ],
        []
    )



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

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

        return (
          <>
            <input type="checkbox" ref={resolvedRef} {...rest} />
          </>
        )
      }
    )
    

    const {
        getTableProps,
        getTableBodyProps,
        headerGroups,
        page, // Instead of using 'rows', we'll use page,
        // which has only the rows for the active page

        // The rest of these things are super handy, too ;)
        canPreviousPage,
        canNextPage,
        pageOptions,
        pageCount,
        gotoPage,
        nextPage,
        previousPage,
        setPageSize,
        prepareRow,
        state: { pageIndex, pageSize, selectedRowIds },
        visibleColumns,
        preGlobalFilteredRows,
        setGlobalFilter,
        selectedFlatRows
      } = useTable({ columns, data }, useGlobalFilter, useFilters, useSortBy, usePagination, 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: ({ getToggleAllPageRowsSelectedProps }) => (
              <div>
                <IndeterminateCheckbox {...getToggleAllPageRowsSelectedProps()} />
              </div>
            ),
            // The cell can use the individual row's getToggleRowSelectedProps method
            // to the render a checkbox
            Cell: ({ row }) => (
              <div>
                <IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
              </div>
            ),
          },
          ...columns,
        ])
      } )





    return (
      <>
        <DeleteItemButton items={selectedFlatRows} />
      
        <BTable {...getTableProps()} striped bordered hover size="sm">
          <thead>
            {headerGroups.map(headerGroup => (
              <tr {...headerGroup.getHeaderGroupProps()}>
                {headerGroup.headers.map(column => (
                  <th
                    {...column.getHeaderProps(column.getSortByToggleProps())}
                  >
                    {column.render('Header')}
                  </th>
                ))}
              </tr>
            ))}
          </thead>
          <tbody {...getTableBodyProps()}>
            {page.map((row, i) => {
              prepareRow(row)
              return (
                <tr {...row.getRowProps()}>
                  {row.cells.map(cell => {
                    return (
                      <td
                        {...cell.getCellProps()}
                        style={{
                        //   padding: '10px',
                        //   border: 'solid 1px gray',
                        //   background: 'papayawhip',
                        }}
                      >
                        {cell.render('Cell')}
                      </td>
                    )
                  })}
                </tr>
              )
            })}
          </tbody>
        </BTable>
        <div className="pagination">
        <button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
          {'<<'}
        </button>{' '}
        <button onClick={() => previousPage()} disabled={!canPreviousPage}>
          {'<'}
        </button>{' '}
        <button onClick={() => nextPage()} disabled={!canNextPage}>
          {'>'}
        </button>{' '}
        <button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
          {'>>'}
        </button>{' '}
        <span>
          Page{' '}
          <strong>
            {pageIndex + 1} of {pageOptions.length}
          </strong>{' '}
        </span>
        <span>
          | Go to page:{' '}
          <input
            type="number"
            defaultValue={pageIndex + 1}
            onChange={e => {
              const page = e.target.value ? Number(e.target.value) - 1 : 0
              gotoPage(page)
            }}
            style={{ width: '100px' }}
          />
        </span>{' '}
        <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>
      </div>
        </>
      )
}
export default TableResults;


DeleteItemButton 将生成一个运行良好的简单 RTK 查询,并触发来自主组件的数据刷新:

 deleteItems: builder.mutation({
        query(data) {

            // send array
            let sanitized;
            sanitized = keywords.filter(item => item);
            
            const data = {
                items: sanitized
            }
            //console.log('data: ', data);
          return {
            url: `items`,
            method: 'DELETE',
            body: data
          }
        },
        invalidatesTags: ['Items']
      }),

该应用程序在 nextJS 下运行。任何帮助表示赞赏!

谢谢

【问题讨论】:

  • 这是描述您的问题的最小示例吗?

标签: javascript reactjs react-redux rtk-query


【解决方案1】:

生成表刷新的正确“react/redux”方式是什么?

刷新数据的正确方法是使用您已经在使用的 RTK 查询的providesTagsinvalidatesTags 功能。

您的表不刷新的原因是因为您明确告诉它忽略对props.data 的所有更改,并为所有后续渲染使用初始值。这显然不是您想要的,但这是您在这里所做的:

const data = React.useMemo(
        () => props.data,
        []
      )

具有空依赖数组的useMemo 只会执行一次。一般来说,您在useMemo 中使用的所有变量都应作为依赖项包含在内。所以依赖数组应该是[props.data]

当然这个特别的useMemo做任何事情并且可以安全地删除。如果您正在对props.data 变量进行一些重新格式化并且只想在props.data 的值发生变化时执行重新格式化,那么拥有useMemo 是有意义的。

const data = React.useMemo(
   () => someExpensiveComputation(props.data),
   [props.data]
);

【讨论】:

    猜你喜欢
    • 2019-06-11
    • 2018-06-08
    • 1970-01-01
    • 2022-10-07
    • 1970-01-01
    • 2020-04-01
    • 2021-06-29
    • 2023-03-18
    • 2018-11-19
    相关资源
    最近更新 更多