React.memo
uses a shallow comparison to determine if the props are equal, but I need to pass an object or array as prop, so I went into an areEqual
condition, but the currentProps
and nextProps
values are always the same. I mean, the component does not render.
Lets say this:
export default function App() {
const [data, setData] = useState([
{
name: "First name",
amount: 0
},
{
name: "Other name",
amount: 0
}
]);
const [value, setValue] = useState("");
return (
<>
<input
type="text"
placeholder="Type in for test"
value={value}
onChange={(e) => {
setValue(e.target.value);
}}
/>
<br />
<input
type="button"
value="Click to increment first!"
onClick={() => {
const temp = [...data];
temp[0].amount += 1;
setData(temp);
}}
/>
<input
type="button"
value="Click to increment other!"
onClick={() => {
const temp = [...data];
temp[1].amount += 1;
setData(temp);
}}
/>
<br />
<Child data={data} />
</>
);
}
and
const Child = ({ data }) => {
const count = useRef(0);
return (
<>
{data &&
data.map((obj, index) => {
return obj.name + "-" + obj.amount;
})}
<br />
Count: {count.current++}
</>
);
};
const areEqual = (currentProps, nextProps) => {
console.log(currentProps.data[0].amount, nextProps.data[0].amount);
console.log(currentProps.data[1].amount, nextProps.data[1].amount);
if (
currentProps.data[0].amount === nextProps.data[0].amount &&
currentProps.data[1].amount === nextProps.data[1].amount
) {
return true;
}
return false;
};
export default memo(Child, areEqual);
but no matter what always currentProps and nextProps are returning the very same value:
Everything is on this sandbox. What am I missing here?
The problem is with the object mutation. Create a new object reference instead.
Don't
const temp = [...data];
temp[0].amount += 1;
setData(temp);
Do
setData(
data.map((item, index) =>
index === 0 ? { ...item, amount: item.amount + 1 } : item
)
);
Also works (using a temporary variable)
If you prefer the mutation style of using a temp
variable, you should avoid using the same object reference:
const temp = [...data];
temp[0] = { ...temp[0], amount: temp[0].amount + 1 };
setData(temp);
Why?
but no matter what always currentProps and nextProps are returning the very same value
currentProps
and nextProps
are different (the references of the data
prop are different). You can check it by adding console.log(currentProps.data === nextProps.data)
to your areEquals
function (it will return false
).
By reusing the same object references, when you make the mutation on one object (for instance the one at index 0) it gets updated in both currentProps.data[0]
and nextProps.data[0]
. You can check it by adding console.log(currentProps.data[0] === nextProps.data[0])
to your areEquals
function (it will return true
).