[英]React Memo resets values in component state
What I am trying to do我想要做什么
The Issue I'm facing我面临的问题
Whenever a card is marked as done, the header changes alright, but whenever any other card is marked as done, the first card's state gets reverted back.每当一张卡被标记为完成时,header 就会正常更改,但只要任何其他卡被标记为完成,第一张卡的 state 就会恢复原状。
I've not been able to find other people facing a similar issue, can somebody please help?我找不到其他面临类似问题的人,有人可以帮忙吗?
Here is the code:这是代码:
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",
description: "",
startDate: "2021-03-13",
doneTasksOn: ["2021-03-14"]
},
{
id: "1615649401885",
name: "Swimming",
description: "",
startDate: "2021-03-13",
doneTasksOn: []
},
{
id: "1615702630514",
name: "Arts",
description: "",
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);
Note: This works fine without using React.memo() wrapping on the Card component.注意:这在不使用 React.memo() 包装 Card 组件的情况下可以正常工作。
Here is the codesandbox link: https://codesandbox.io/s/winter-water-c2592?file=/src/App.js这是代码框链接: https://codesandbox.io/s/winter-water-c2592?file=/src/App.js
Problem is because of your (custom) memoization markHabitDone
becomes a stale closure in some components.问题是因为您的(自定义)记忆markHabitDone
在某些组件中变成了一个陈旧的闭包。
Notice how you pass markHabitDone
to components.请注意如何将markHabitDone
传递给组件。 Now imagine you click one of the cards and mark it as done.现在假设您单击其中一张卡片并将其标记为完成。 Because of your custom memoization function other cards won't be rerendered, hence they will still have an instance of markHabitDone
from a previous render .由于您的自定义记忆 function其他卡片不会被重新渲染,因此它们仍然会有一个来自先前渲染的markHabitDone
实例。 So when you update an item in a new card now:因此,当您现在更新新卡中的项目时:
let newHabits = [...habits];
the ...habits
there is from previous render .来自先前渲染的...habits
。 So the old items are basically re created this way.所以旧的物品基本上都是这样重新创建的。
Using custom functions for comparison in memo
like your areCardEqual
function can be tricky exactly because you may forget to compare some props and be left with stale closures.在memo
中使用自定义函数进行比较,例如areCardEqual
function 可能会很棘手,因为您可能会忘记比较一些道具并留下陈旧的闭包。
One of the solutions is to get rid of the custom comparison function in memo
and look into using useCallback
for the markHabitDone
function.解决方案之一是在memo
中去掉自定义比较 function 并考虑使用useCallback
来表示markHabitDone
function。 If you also use []
for the useCallback
then you must rewrite markHabitDone
function (using the functional form of setState
) such that it doesn't read the habits
using a closure like you have in first line of that function (otherwise it will always read old value of habits
due to empty array in useCallback
).如果您还使用[]
作为useCallback
那么您必须重写markHabitDone
function (使用setState
的函数形式),这样它就不会像在 function 的第一行中那样使用闭包来读取habits
(否则它将始终读取由于useCallback
中的空数组而导致habits
的旧值)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.