I am using useMemo hook to render map items.I added items parameter to useMemo hook, based on items change it will render. But changing loading state and items change, Item custom component rendering twice. Am i doing any mistake on using useMemo hook, please correct me.
Home:
import React, { useState, useEffect, useMemo } from "react";
import Item from "./Item";
const array = [1];
const newArray = [4];
const Home = () => {
const [items, setItems] = useState(array);
const [loading, setLoading] = useState(false);
const [dataChange, setDataChange] = useState(1);
const renderItems = (item, index) => {
return (
<div key={item}>
<Item id={item}></Item>
</div>
);
};
useEffect(() => {
if (dataChange === 2) {
setLoading(true);
setTimeout(() => {
setLoading(false);
setItems(newArray);
}, 3000);
}
}, [dataChange]);
const memoData = useMemo(() => {
return <div>{items.map(renderItems)}</div>;
}, [items]);
return (
<div style={{ display: "flex", flexDirection: "column" }}>
<input
onClick={() => {
setDataChange(2);
}}
style={{ height: 40, width: 100, margin: 20 }}
type="button"
value="ChangeItem"
></input>
<div>{loading ? <label>{"Loading"}</label> : <div>{memoData}</div>}</div>
</div>
);
};
export default React.memo(Home);
Item:
import React,{useEffect} from "react";
const Item = (props) => {
console.log("props", props);
useEffect(() => {
// call api with props.id
}, [props]);
return <div>Hello world {props.id}</div>;
};
export default React.memo(Item);
Result:
first time :
props {id: 1}
After click :
props {id: 1}
props {id: 4}
If you want to optimize useMemo you can use object properties in the useMemo dependency array.
Why not use useMemo everywhere then? In short, it's not a free performance optimisation. There's an additional cost (memory usage, for one) incurred when setting up useMemo , that can very quickly outweigh the performance benefit of remembering every single function's possible value.
React has a built-in hook called useMemo that allows you to memoize expensive functions so that you can avoid calling them on every render. You simple pass in a function and an array of inputs and useMemo will only recompute the memoized value when one of the inputs has changed.
useCallback and useMemo for props don't prevent re-renders by themselves. Only when every single prop and the component itself are memoized, then re-renders can be prevented.
In the above example, the useMemo () hook accepts two arguments: 1 The first is a callback function that will run the function to be memoized 2 The second is a dependency array that will be observed by the hook More ...
React Hook "React.useMemo" is called in function "getCols" which is neither a React function component or a custom React Hook function Why isn't it a React function?
`useMemo` allows you to memoize the results of a function, and will return that result until an array of dependencies change. <!-- In this example, a component receives a list of widgets.
If an empty dependencies array [] is provided, useMemo will only execute the function once on the very first render of the component, while referring to the memoized value for the remainder of the component’s lifecycle. useMemo is especially useful when you are dealing with potentially large lists of items.
There are a few things which are not right in the code above.
key
should be passed to the parent element in an array iteration - in your case the renderItems
should pass the key
to the div
elementloading
state before updating the items
array, switching the two setState
expressions will resolve your case most of the time although setState
is an async function and this is not guaranteedrenderItems
Here's why there is one more console.log executed
useMemo
with a React.memo
component which takes care of the array because it is kept in the state and it's reference won't change on rerender if the state remains the same const array = [1];
const newArray = [4];
const Home = () => {
const [items, setItems] = useState(array);
const [loading, setLoading] = useState(false);
const [dataChange, setDataChange] = useState(1);
useEffect(() => {
if (dataChange === 2) {
setLoading(true);
setTimeout(() => {
setItems(newArray);
setLoading(false);
}, 3000);
}
}, [dataChange]);
return (
<div style={{ display: "flex", flexDirection: "column" }}>
<input
onClick={() => {
setDataChange(2);
}}
style={{ height: 40, width: 100, margin: 20 }}
type="button"
value="ChangeItem"
></input>
<div>
{loading ? <label>{"Loading"}</label> : <ItemsMemo items={items} />}
</div>
</div>
);
};
const renderItems = (item) => {
return (
<span key={item} id={item}>
{item}
</span>
);
};
const Items = ({ items }) => {
console.log({ props: items[0] });
return (
<div>
Hello world <span>{items.map(renderItems)}</span>
</div>
);
};
const ItemsMemo = React.memo(Items);
UPDATE
This codesandbox shows that useMemo
gets called only when the items
value changes as it is supposed to do.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With