如何解决React Memo 重置组件状态中的值
我想做什么
- 我正在尝试使用一组对象(习惯)并从每个对象(习惯)渲染一个“卡片”组件。
- 现在,对于每张卡片(习惯),您都可以将该习惯“标记”为已完成,这将更新卡片的状态。
- 我使用 React.memo 来防止其他卡片重新渲染。
- 每当用户将卡片标记为完成时,卡片标题就会更改为“已编辑”
我面临的问题
每当一张卡片被标记为完成时,标题就会改变,但是每当任何其他卡片被标记为完成时,第一张卡片的状态就会恢复。
我找不到其他面临类似问题的人,有人可以帮忙吗?
代码如下:
import React,{ useState } from "react";
const initialState = {
habits: [
{
id: "1615649099565",name: "Reading",description: "",startDate: "2021-03-13",doneTasksOn: ["2021-03-13"]
},{
id: "1615649107911",name: "Workout",doneTasksOn: ["2021-03-14"]
},{
id: "1615649401885",name: "Swimming",doneTasksOn: []
},{
id: "1615702630514",name: "Arts",startDate: "2021-03-14",doneTasksOn: ["2021-03-14"]
}
]
};
export default function App() {
const [habits,setHabits] = useState(initialState.habits);
const markHabitDone = (id) => {
let newHabits = [...habits];
let habitToEditIdx = undefined;
for (let i = 0; i < newHabits.length; i++) {
if (newHabits[i].id === id) {
habitToEditIdx = i;
break;
}
}
let habit = { ...habits[habitToEditIdx],doneTasksOn: [],name: "Edited" };
newHabits[habitToEditIdx] = habit;
setHabits(newHabits);
};
return (
<div className="App">
<section className="test-habit-cards-container">
{habits.map((habit) => {
return (
<MemoizedCard
markHabitDone={markHabitDone}
key={habit.id}
{...habit}
/>
);
})}
</section>
</div>
);
}
const Card = ({
id,name,description,startDate,doneTasksOn,markHabitDone
}) => {
console.log(`Rendering ${name}`);
return (
<section className="test-card">
<h2>{name}</h2>
<small>{description}</small>
<h3>{startDate}</h3>
<small>{doneTasksOn}</small>
<div>
<button onClick={() => markHabitDone(id,name)}>Mark Done</button>
</div>
</section>
);
};
const areCardEqual = (prevProps,nextProps) => {
const matched =
prevProps.id === nextProps.id &&
prevProps.doneTasksOn === nextProps.doneTasksOn;
return matched;
};
const MemoizedCard = React.memo(Card,areCardEqual);
注意:这可以正常工作,无需在 Card 组件上使用 React.memo() 包装。
这是代码和框链接: https://codesandbox.io/s/winter-water-c2592?file=/src/App.js
解决方法
问题是因为您的(自定义)记忆 markHabitDone
在某些组件中变成了陈旧的闭包。
注意您如何将 markHabitDone
传递给组件。现在假设您单击其中一张卡片并将其标记为已完成。由于您的自定义记忆功能,其他卡片将不会被重新渲染,因此它们仍将具有 markHabitDone
来自先前渲染的实例。因此,当您现在更新新卡片中的项目时:
let newHabits = [...habits];
那里的 ...habits
来自之前的渲染。所以老项目基本上都是这样重新创建的。
在 memo
中像使用 areCardEqual
函数一样使用自定义函数进行比较可能会很棘手,因为您可能会忘记比较一些 props 并留下陈旧的闭包。
解决方案之一是去掉 memo
中的自定义比较函数,并考虑将 useCallback
用于 markHabitDone
函数。如果您还对 []
使用 useCallback
,那么您必须重写 markHabitDone
函数(使用 setState
的 functional 形式),使其不读取habits
使用闭包,就像在该函数的第一行一样(否则,由于 habits
中的数组为空,它将始终读取 useCallback
的旧值)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。