skip to Main Content

So i have this Home Component, where there is a records state, i use it to perform a records.map() and return RecordItem components inside the table.

function Home() {
    const [records, setRecords] = useState<Array<RecordType>>(loadRecords());

    const removeRecord = useCallback((record: RecordType) => {
        setRecords(records => {
            const newRecords = records.filter(rec => rec !== record);
            localStorage.setItem('controle_financeiro', JSON.stringify(newRecords));

            return newRecords;
        });
    }, []);

    return (
        <tbody>
            {
                records.map((record, index) => <RecordItem key={index} record={record} removeRecord={removeRecord}/>)
            }
        </tbody>
    )
}

And there is the RecordItem component, that is used by the Home component.
As you can see, there is a button, that executes the removeRecord function on click.
That function is passed as props, so in the Home component, it is a memoized function.

function RecordItem({record, removeRecord}: RecordProps) {
    console.log('Renderized RecordItem'); // if there is 50 items in the `records` state array, it will print 50x
    
    return (
      <tr>
          <td>{record.description}</td>
          <td>{record.value}</td>
          <td>{record.type === 'in' ? <FaRegArrowAltCircleUp className='in-icon'/> : <FaRegArrowAltCircleDown className='out-icon'/>}</td>
          // button that executes the removeRecord function
          <td> <FaTrash className='remove-icon' onClick={e => removeRecord(record)}/> </td>
      </tr>
    )
};
  
export default memo(RecordItem);

So, if there is 50 items in the records state, in the Home component, it will print in the console Renderized RecordItem 50x.
It render the entire array again, i don’t want it. I did anything wrong?

2

Answers


  1. key={index}
    

    The issue is that you’re using the index as a key.

    Suppose you delete the first item in the array. You’re expecting react to remove the first item from the page, and leave the rest unchanged, but the keys say to do something else. On one render there’s a list of 50 items with keys 0-49, then on the next render there’s a list of 49 items with keys 0-48. Based on this, react will remove the last element, because that’s the only one who’s key is not there any more. For all the other keys, the props have changed: key 0 is getting the record that used to be in key 1, key 1 is getting the record from key 2, etc. So all of these components receive new props, breaking their memo.

    To fix this, you’ll need some unique identifier on your records, and use that as the key. If you already have a value on RecordType, use that. If not, you’ll need to add one. Perhaps the loadRecords function can do that.

    Login or Signup to reply.
  2. {records.map((record, index) => <RecordItem key={index} record={record} removeRecord={removeRecord}/>)}
    

    don’t use the index as a key.
    In that case, All of items are re-rendered if you remove one by clicking the button. because index is changed.
    So you have to use other unique data as a key.
    Does record has a unique id or something else?.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search