Home > database >  How do I add rows to a table in React Hooks without getting duplicates?
How do I add rows to a table in React Hooks without getting duplicates?

Time:11-19

I'm attempting to create a data table with react hooks but I keep getting duplicates rows in my state. Here is the call where I'm getting values doing some manipulation and then calling my function to update state:

var tableRowIndex = 0;
const CoinTable = () => {
    const baseURL = 'endpoint/';
    const [tableRows, setRows] = useState([] as any);


    const getCurrentPrice = async (inputs: any) => {
        const response = await axios.get(`${baseURL}${inputs.currency}/USD`)
        let currentPrice = response.data
        inputs.cryptoPrice = currentPrice.rate;
        let coinsRequired = inputs.amount / inputs.cryptoPrice;
        inputs.amtCrypto = coinsRequired.toFixed(8);
        addNewRow(inputs)
    }

Here is my function where I'm attempting to update state

 const addNewRow = (inputs: any) => {
        tableRowIndex  
        inputs.index = tableRowIndex
        setRows([...tableRows, inputs])
    }

This is the rest of the components where I'm mapping through my rows and outputting in my JSX.

const rows = tableRows.map((row: any) => {
        return (
            <TableRow
                key={tableRowIndex}
                addNewRow={addNewRow}
                removeRow={removeRowHandler}
                getCurrentPrice={getCurrentPrice}
                row={row}
            />
        )
    })
    
    return (
        <>
            <AddItem
                getCurrentPrice={getCurrentPrice}
            />
            {tableRows.length > 0 &&
                <table>
                    <tbody>
                        <tr>
                            <th>Merchant</th>
                            <th>Item</th>
                            <th>Amount(Crypto)</th>
                            <th>Currency</th>
                            <th>Price/crypto(USD)</th>
                            <th>Amount(USD)</th>
                        </tr>
                        {rows}
                    </tbody>
                </table>

            }
        </>
    )
}

export default CoinTable;

Inputs is object containing user inputs to be rendered as a new row. It appears to be an issue as to how I'm updating state using the spread operator but I'm not sure.

CodePudding user response:

It appears as though you are using the single tableRowIndex "global" value as the React key for every mapped element. You likely meant to use the row indexgenerated inaddNewRowwhen adding an element to thetableRows` state.

const addNewRow = (inputs: any) => {
  tableRowIndex  ;
  inputs.index = tableRowIndex; // <-- assigned here
  setRows([...tableRows, inputs]);
}

...

const rows = tableRows.map((row: any) => {
  return (
    <TableRow
      key={row.index} // <-- used here
      addNewRow={addNewRow}
      removeRow={removeRowHandler}
      getCurrentPrice={getCurrentPrice}
      row={row}
    />
  )
})

A more idiomatic method would be to call this augmented property id so it's abundantly clear it's not any array index and actually a unique value assigned to each element. I'd even go as far as to say you might want to use a library that generates GUIDs for you. uuid is a great one.

import { v4 as uuidV4 } from 'uuid';

...

const addNewRow = (inputs: any) => {
  setRows(rowData => [
    ...rowData,
    {
      ...inputs,   // <-- don't mutate inputs object
      id: v4uuid() // <-- assign unique id
    },
  ]);
}

...

const rows = tableRows.map((row: any) => {
  return (
    <TableRow
      key={row.id}
      addNewRow={addNewRow}
      removeRow={removeRowHandler}
      getCurrentPrice={getCurrentPrice}
      row={row}
    />
  )
})
  • Related