Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

React memo and/or useCallback not working as expected

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?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading