Pesquisa de site

reactjs - Como posso parar a re-renderização do TanStack React Table até que o campo de entrada esteja totalmente preenchido?


Estou criando uma tabela na qual o preço unitário pode ser inserido em uma coluna e outra coluna calcula o preço total com base no valor inserido e no valor de outra coluna "Quantidade" (preço total=qtd * preço unitário). Eu implementei isso em uma tabela normal no React sem nenhuma biblioteca, mas queria implementar filtragem e ordenação, então decidi migrar para o TanStack Table.

O problema é que o elemento de entrada fica desselecionado assim que eu digito até mesmo um caractere na entrada. Descobri que a razão pode ser que a tabela é re-renderizada assim que eu digito até mesmo um caractere, então não consigo inserir toda a entrada. Como resolver esse problema?

const defaultColumns = [

    // Accessor Column
    columnHelper.accessor("totalQty", {
      cell: (info) => info.getValue(),
      footer: (props) => props.column.id,
      header: () => <span>Quantity</span>,
    }),
    // ...other columns
    // Accessor Column
    columnHelper.accessor("unitPrice", {
      header: "Unit Price",
      footer: (props) => props.column.id,
      cell: (props) => (
        <input
          type="number"
          className="number-input"
          value={props.row.original.unitPrice}
          onChange={(e) => {
            handleUnitPriceChange(props.row.index, Number(e.target.value));
          }}
        />
      ),
    }),
    // Accessor Column
    columnHelper.accessor(
      (row) => {
        return row.totalQty ? row.totalQty * row.unitPrice : null;
      },
      {
        header: "Total Price",
        footer: (props) => props.column.id,
        cell: (props) => (
          <span>
            {props.row.original.totalQty
              ? props.row.original.totalQty * props.row.original.unitPrice
              : null}
          </span>
        ),
      }
    ),
  ];
  const table = useReactTable({
    data,
    columns: defaultColumns,
    getCoreRowModel: getCoreRowModel(),
  });

Esta é a função que atualiza o preço unitário na itemList:

function handleUnitPriceChange(index: number, up: number | null) {
    const updatedPurchaseReq = { ...purchaseReq };
    if (up !== null) {
      updatedPurchaseReq.itemList[index].unitPrice = up;
      updatedPurchaseReq.itemList[index].totalCost =
        up * updatedPurchaseReq.itemList[index].totalQty;
    } else {
      updatedPurchaseReq.itemList[index].unitPrice = 0;
      updatedPurchaseReq.itemList[index].totalCost = 0;
    }
    setPurchaseReq(updatedPurchaseReq);
  }

Como corrigir o fato de o campo de entrada ser desmarcado após cada caractere digitado?

Eu tentei corrigir a re-renderização usando useMemo, mas não funcionou. A tabela se re-renderiza assim que um caractere é digitado.

Resposta #1

Depois de precisar de uma resposta para isso eu mesmo, acabei dando uma olhada no comentário de Iorweth333 sobre Material React Table para ver como eles fizeram.

Acontece que praticamente tudo o que eles fazem é baseado em onBlur e não em onChange, então consegui chegar a esta solução usando React Hook Form e Shadcn no meu caso:

No seu caso, eu provavelmente faria essas mudanças e veria se funciona (embora este post seja de dois meses atrás agora):

columnHelper.accessor("unitPrice", {
  header: "Unit Price",
  footer: (props) => props.column.id,
  cell: (props) => (
    <input
      type="number"
      className="number-input"
      defaultValue={props.row.original.unitPrice}
      onBlur={(e) => {
        handleUnitPriceChange(props.row.index, Number(e.target.value));
      }}
    />
  ),
}),

Caso outra pessoa tenha meu problema específico, aqui está o que eu fiz:

{
  accessorKey: 'amount',
  header: 'Amount',
  cell: ({ row }) => {
    const index = row.index
    return (
      <FormField
        control={form.control}
        name={`invoiceGlCodes.${index}.amount`}
        render={({ field: { onChange, onBlur, value, ...fieldProps } }) => (
          <FormItem>
            <FormControl>
              <Input
                type="text"
                defaultValue={value}
                onBlur={(event) => {
                  onChange(event)
                  onBlur()
                }}
                {...fieldProps}
              />
            </FormControl>
            <FormMessage />
          </FormItem>
        )}
      />
    )
  },
},

Também observe que eu uso defaultValue em vez de value, porque isso permitirá que você atualize o input antes de salvá-lo no estado.