reactjs React memo和/或useCallback未按预期工作

yqkkidmi  于 2023-04-05  发布在  React
关注(0)|答案(2)|浏览(142)

所以我有这个Home Component,其中有一个records状态,我用它来执行records.map()并返回表中的RecordItem组件。

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>
    )
}

还有一个RecordItem组件,它被Home组件使用。正如你所看到的,有一个按钮,它在点击时执行removeRecord函数。该函数作为props传递,因此在Home组件中,它是一个memoized函数。

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);

所以,如果在records状态下有50个项目,在Home组件中,它将在控制台中打印Renderized RecordItem 50x。它再次呈现整个数组,我不想要它。我做错了什么吗?

d4so4syb

d4so4syb1#

key={index}

问题是你使用索引作为键。
假设你删除了数组中的第一个元素。你希望react从页面中删除第一个元素,其余的元素保持不变,但是键告诉你要做别的事情。在一个渲染器上,有一个50个元素的列表,键为0-49,然后在下一个渲染器上,有一个49个元素的列表,键为0-48。基于此,react将删除 last 元素,因为这是唯一一个钥匙不在那里的人。对于所有其他的钥匙, prop 已经改变了:键0得到了原来在键1中的记录,键1得到了键2的记录,以此类推。所以所有这些组件都得到了新的 prop ,打破了它们的备忘录。
为了解决这个问题,你需要在你的记录上有一些唯一的标识符,并使用它作为键。如果你已经在RecordType上有一个值,使用它。如果没有,你需要添加一个。也许loadRecords函数可以做到这一点。

hwamh0ep

hwamh0ep2#

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

不要使用index作为关键字。在这种情况下,如果您通过单击按钮删除一个项目,则所有项目都会重新呈现。因为索引已更改。因此您必须使用其他唯一数据作为关键字。record是否具有唯一ID或其他内容?。

相关问题