Update React Hooks State During Render Using useMemo - reactjs

Can useMemo be used just to avoid extra referential equality checking code/vars when setting state during a render?
Example: useMemo with a setState during render taken from this rare documented use case:
function ScrollView({row}) {
let [isScrolling, setIsScrolling] = useState(false);
const lessCodeThanCheckingPrevRow = useMemo(
() => {
// Row changed since last render. Update isScrolling.
setIsScrolling(true); // let's assume the simplest case where prevState isn't needed here
},
[row]
);
return `Scrolling down: ${isScrolling}`;
}
Above drastically reduces code and extra vars, only otherwise used for equality checks, so why do the docs imply referential equality checks should be done manually?

This seems to be an elegant way to reduce boiler plate to me. I created a codesandbox to validate its behaviour.
const UnitUnderTest = ({prop}) => {
let [someState, setSomeState] = useState(false);
const lessCodeThanCheckingPrevRow = useMemo(
() => setSomeState(current => !current),
[prop],
);
useEffect(() => console.log('update finished'), [prop])
console.log('run component');
return `State: ${someState}`;
}
const App = () => {
const [prop, setProp] = useState(false);
const handleClick = () => setProp(current => !current);
return (
<div>
<button onClick={handleClick} >change prop</button>
<UnitUnderTest prop={prop} />
</div>
)
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Output when clicking the button to change the prop passed to the component:
> run component
> run component
> update finished
As you can see the component has been run twice before the update cycle completed. This is equivalent to the the behaviour of getDerivedStateFromProps.
I guess that there is no deeper thought behind why the docs propose a slightly different technique. In a way this is a manual check too but in a neat way. +1 for the idea.

Use the useEffect hook for this behavior. useMemo is used to store a value that might not necessarily change over each renders, so that you avoid useless re-calculation of that value

if the problem is having a value that changes on every user action which in this case is scrolling, then useMemo is not the best way to go. Perhaps useCallback is a better option. The code block would be the same as you have defined it above, but instead of useMemo, useCallback, and include the state in the dependency array.

Related

React hooks : how can I update a state within useEffect when the state itself is the dependency?

I know there already are already some related questions, like How can React useEffect watch and update state?, but still, I don't get it totally.
Let's say I set an index state based on a prop; and I need to sanitize that value anytime it is set.
<MyComponent index={4}/>
This is how I attempted to do it:
useEffect(() => {
setIndex(props.index);
}, [props.index]);
useEffect(() => {
const sanitized = sanitizeIndex(index);
setIndex(sanitized);
},[index])
const sanitizeIndex = index => {
//check that index exists in array...
//use fallback if not...
//etc.
return index
}
It does not work (infinite loop), since the state is watched and updated by the second useEffect().
Of course, I could avoid this by calling sanitizeIndex() on the prop, so I only need a single instance of useEffect():
useEffect(() => {
setIndex(sanitizeIndex(props.index));
}, [props.index]);
Thing is, I call setIndex plenty of times in my code, and I fear to miss using sanitizeIndex.
Is there another way to "catch" and update a state value being set ?
Thanks !
This seems like a good case for a custom hook. Here's an example of how to implement one for your case (given the information currently provided in your question), including comments about how/why:
Be sure to read the documentation for useCallback if you are not already familiar with it. It's especially important to understand how to use the dependency array (link 1, link 2) when using hooks which utilize it (like useCallback and useEffect).
<div id="root"></div><script src="https://unpkg.com/react#17.0.2/umd/react.development.js"></script><script src="https://unpkg.com/react-dom#17.0.2/umd/react-dom.development.js"></script><script src="https://unpkg.com/#babel/standalone#7.16.12/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="env,react">
const {useCallback, useEffect, useState} = React;
/**
* You didn't show exactly how you are sanitizing, so I'm using this function
* instead. It will simply make sure the input number is always even by
* adding 1 to it if it's odd.
*/
function makeEven (n) {
return n % 2 === 0 ? n : n + 1;
}
function useSanitizedIndex (sanitizeIndex, unsanitizedIndex) {
const [index, setIndex] = useState(sanitizeIndex(unsanitizedIndex));
// Like setIndex, but also sanitizes
const setSanitizedIndex = useCallback(
(unsanitizedIndex) => setIndex(sanitizeIndex(unsanitizedIndex)),
[sanitizeIndex, setIndex],
);
// Update state if arguments change
useEffect(
() => setSanitizedIndex(unsanitizedIndex),
[setSanitizedIndex, unsanitizedIndex],
);
return [index, setSanitizedIndex];
}
function IndexComponent (props) {
// useCallback memoizes the function so that it's not recreated on every
// render. This also prevents the custom hook from looping infinintely
const sanitizeIndex = useCallback((unsanitizedIndex) => {
// Whatever you actually do to sanitize the index goes in here,
// but I'll just use the makeEven function for this example
return makeEven(unsanitizedIndex);
// If you use other variables in this function which are defined in this
// component (e.g. you mentioned an array state of some kind), you'll need
// to include them in the dependency array below:
}, []);
// Now simply use the sanitized index where you need it,
// and the setter will sanitize for you when setting it (like in the
// click handler in the button below)
const [index, setSanitizedIndex] = useSanitizedIndex(sanitizeIndex, props.index);
return (
<div>
<div>Sanitized index (will always be even): {index}</div>
<button onClick={() => setSanitizedIndex(5)}>Set to 5</button>
</div>
);
}
function Example () {
const [count, setCount] = useState(0);
return (
<div>
<div>Count: {count}</div>
<button onClick={() => setCount(n => n + 1)}>Increment</button>
<IndexComponent index={count} />
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
</script>
So think of useEffect like an event listener in javascript. It's not the same thing, but think of it like that. The event or "what's being watched", in this case, you've asked it to watch props.index. It will run that function inside the useEffect every time anything in the dependency array (props.index - in your case) changes. So what's happening here is you're updating props.index every time props.index changes. This is your infinite loop.
Couple things here, create a copy of props.index as something ie.
const [something, setSomething = useState(props.index);
(I won't get into destructuring, but worth looking that up too)
You don't want to manipulate your props directly the way you are doing.
This solves that, as well as gives you the correct way to look at your useEffect. Since you want to update something whenever that prop changes, you could leave props.index (again look up destructuring) in your dependency array, and change the useEffect to:
const [something, setSomething] = useState(props.index);
useEffect(() => {
setSomething(props.index);
}, [props.index]);
As another pointed out, this is difficult without knowing exactly what you're doing, but this is kind of an overview which hopefully helps you understand what is going on here and why you're getting a loop here.
You mentioned you fear missing out on sanitizing, then you should not be using setIndex directly. Instead, you can create a new function to santize and set the index.
useEffect(() => {
setSanitizeIndex(props.index);
}, [props.index]);
const setSanitizeIndex = (value) => {
const sanitizeIndex = sanitizeIndex(value);
setIndex(sanitizeIndex)
}
With that, you should not be calling setIndex any more in your codes, but only call setSanitizeIndex.
One potential solution for this that I have done in the past with a similar issue is to indirectly trigger the useEffect. Create a dummy state that does not relate to the state being updated and update the dummy state whenever you want the effect to be triggered.
const [dummyState, setDummyState] = useState(0)
useEffect(() => {
setIndex(props.index);
}, [props.index]);
useEffect(() => {
const sanitized = sanitizeIndex(index);
setIndex(sanitized);
},[dummyState])
const sanitizeIndex = index => {
//check that index exists in array...
//use fallback if not...
//etc.
return index
}
return (
//update the state you want to update then update the dummy state to
//trigger the useEffect
<button onClick={() =>{setIndex(123);
setDummyState(dummyState++);}/>
)
I think the accepted answers solution is a lot less janky but in more complex situations this might be your easiest and most easy-to-understand solution

is useCallback a performance boost for the react app?

could someone explain to me if the use of useMemo and useCallback is expensive or cheap? I've checked the React's source code and I think they are cheap to use, but I've also read some people saying they are not.
In a Next.js context using useCallback:
const MyComp = () => {
const router = useRouter():
const handleClick = useCallback(() => router.push('/some-path'), [router]);
return <button onClick={handleClick} />
}
vs plain arrow function here:
const MyComp = () => {
const router = useRouter():
return <button onClick={() => router.push('/some-path')} />
}
Am I saving re-renders with useCallback?
The cost of memoize and comprare the dependencies array [router], is more expensive?
Additional info: Checking the React's code I saw that they compare the deps array items using Object.is instead of === or ==.
Someone knows why?
Also checking this component render:
import {useCallback, useState} from "react";
let i = 0
const CallbackDemo = () => {
const [value, setValue] = useState('');
console.log('render', i++);
const handleClick = useCallback(() => setValue(Math.random()), []);
return (
<div>
<span>{value}</span>
<button onClick={handleClick}>New set</button>
</div>
);
}
export default CallbackDemo;
I saw the same count of renders using or not using useCallback
Am I saving re-renders with useCallback?
Not in that specific case, no. The useCallback code does let you save roughly the cost of an assignment statement because it doesn't have to update the click handler on the button element, but at the cost of a function call and going through the array of dependencies looking for differences. So in that specific case, it's probably not worth doing.
If you had a more complex child component, and that component was optimized not to re-render when called with the same props (for instance, via React.memo or React.PureComponent or similar), or you were passing the function to a bunch of child components, then you might get some performance improvements by not making them re-render.
Consider this example, where simpleClickHandler is always recreated but memoizedClickHandler is reused¹ via useCallback:
const { useState, useCallback } = React;
const Parent = () => {
const [counter, setCounter] = useState(0);
console.log("Parent called");
const simpleClickHandler = () => {
console.log("Click occurred");
setCounter(c => c + 1);
};
const memoizedClickHandler = useCallback(() => {
console.log("Click occurred");
setCounter(c => c + 1);
}, []);
return (
<div>
Count: {counter}
<Child onClick={simpleClickHandler} handlerName="simpleClickHandler">simpleClickHandler</Child>
<Child onClick={memoizedClickHandler} handlerName="memoizedClickHandler">memoizedClickHandler</Child>
</div>
);
};
const Child = React.memo(({handlerName, onClick, children}) => {
console.log(`Child using ${handlerName} called`);
return (
<div onClick={onClick}>{children}</div>
);
});
ReactDOM.render(<Parent/>, document.getElementById("root"));
<div id="root"><?div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
Note that when you click, only the child being passed simpleClickHandler re-renders, not the one being passed memoizedClickHandler, because memoizedClickHandler's value is stable but simpleClickHandler's value changes every time.
Using useCallback requires more work in the parent (checking to see if the previous function can be reused), but may help save work in child components.
It's important to note that the stability useCallback (and useMemo) give you are only appropriate for performance reasons, not correctness. React can throw away the previous copy of a handler and new a new one if it wants to, even if the deps haven't changed. If you need a correctness guarantee (the result definitely 100% will not change unless the deps change), you have use a ref. But that's a very rare use case.
Additional info: Checking the React's code I saw that they compare the deps array items using Object.is instead of === or ==.
Someone knows why?
Primarily because NaN === NaN is false, because all comparisons with NaN are false. So if they used ===, any deps array containing NaN would always be considered different from the previous one. But using Object.is avoids that problem, because Object.is(NaN, NaN) is true.
¹ Note that "reused" here means: a new handler is created every time, just like simpleClickHandler, but useCallback may return the previous handler you've given it if the deps haven't changed, so the new one is thrown away. (JavaScript engines are really quick at allocating and reclaiming short-lived objects.)
useCallback like useMemo indeed improve performance
but!!! you don't need to use it to everything because it will make your website slower.
this is better for the heavy lifting components like for charts and stuff like that.
that consume a lot of resource and take a lot of time to process and this will make this specific component load much more faster and not stuck everytime you do a change.
you can see deep compression like this:
react useEffect comparing objects
this link will show you for instance how to do a deep compression for use effect

Can't get `lodash.debounce()` to work properly? Executed multiple times... (react, lodash, hooks)

I am trying to update the state of a child component in React as the range input moves. And, I want to fire the update function to the parent component's state with Lodash's debounce function so that I don't set the state of the parent component every time range input fires an event.
However, after debounce's delay, all the events are getting fired. As if I called setTimeout functions consecutively on every range input event, but not debounce.
I can't find what am I missing here. How can I have the function passed into "debounce" get executed once after a series of range input events?
My simplified code looks like this:
import _ from 'lodash'
import React from 'react'
const Form: React.FC<Props> = props => {
const [selectedStorageSize, setSelectedStorageSize] = React.useState(props.storageSize)
const handleChangeAt = (field, payload) => {
props.handleFormChangeAt(FormField.InstanceDefs, {
...form[FormField.InstanceDefs],
[field]: payload,
})
}
const debouncedChange = _.debounce(
(field, payload) => handleChangeAt(field, payload),
500,
)
return(
<input
required
type="range"
label="Storage Size/GB"
min={50}
max={500}
value={props.selectedStorageSize}
step={5}
onChange={e => {
setSelectedStorageSize(Number(e.target.value))
debouncedChange(FormField.StorageSize, Number(e.target.value))
}}
/>
}
The Problem
_.debounce creates a function that debounces successive calls to the same function instance. But this is creating a new one every render, so successive calls aren't calling the same instance.
You need to reuse the same function across renders. There's a straightforward way to do that with hooks... and a better way to do it:
The straightforward (flawed) solution - useCallback
The most straightforward way of preserving things across render is useMemo/useCallback. (useCallback is really just a special case of useMemo for functions)
const debouncedCallback = useCallback(_.debounce(
(field, payload) => handleChangeAt(field, payload),
500,
), [handleChangeAt])
We've got an issue with handleChangeAt: it depends on props and creates a different instance every render, in order to capture the latest version of props. If we only created debouncedCallback once, we'd capture the first instance of handleChangeAt, and capture the initial value of props, giving us stale data later.
We fix that by adding [handleChangeAt], to recreate the debouncedCallback whenever handleChangeAt changes. However, as written, handleChangeAt changes every render. So this change alone won't change the initial behavior: we'd still recreate debouncedCallback every render. So you'd need to memoize handleChangeAt, too:
const { handleFormChangeAt } = props;
const handleChangeAt = useCallback((field, payload) => {
handleFormChangeAt(/*...*/)
}, [handleFormChangeAt]);
(If this sort of memoizing isn't familiar to you, I highly recommend Dan Abramov's Complete Guide to useEffect, even though we aren't actually using useEffect here)
This pushes the problem up the tree, and you'll need to make sure that whatever component provides props.handleFormChangeAt is also memoizing it. But, otherwise this solution largely works...
The better solution - useRef
Two issues with the previous solution: as mentioned it pushes the problem of memoization up the tree (specifically because you're depending on a function passed as a prop), but the whole point of this is so that we can recreate the function whenever we need to, to avoid stale data.
But the recreating to avoid stale data is going to cause the function to be recreated, which is going to cause the debounce to reset: so the result of the previous solution is something that usually debounces, but might not, if props or state have changed.
A better solution requires us to really only create the memoized function once, but to do so in a way that avoids stale data. We can do that by using a ref:
const debouncedFunctionRef = useRef()
debouncedFunctionRef.current = (field, payload) => handleChangeAt(field, payload);
const debouncedChange = useCallback(_.debounce(
(...args) => debouncedFunctionRef.current(...args),
500,
), []);
This stores the current instance of the function to be debounced in a ref, and updates it every render (preventing stale data). Instead of debouncing that function directly, though, we debounce a wrapper function that reads the current version from the ref and calls it.
Since the only thing the callback depends on is a ref (which is a constant, mutable object), it's okay for useCallback to take [] as its dependencies, and so we'll only debounce the function once per component, as expected.
As a custom hook
This approach could be moved into its own custom hook:
const useDebouncedCallback = (callback, delay) => {
const callbackRef = useRef()
callbackRef.current = callback;
return useCallback(_.debounce(
(...args) => callbackRef.current(...args),
delay,
), []);
};
const { useCallback, useState, useRef, useEffect } = React;
const useDebouncedCallback = (callback, delay, opts) => {
const callbackRef = useRef()
callbackRef.current = callback;
return useCallback(_.debounce(
(...args) => callbackRef.current(...args),
delay,
opts
), []);
};
function Reporter({count}) {
const [msg, setMsg] = useState("Click to record the count")
const onClick = useDebouncedCallback(() => {
setMsg(`The count was ${count} when you clicked`);
}, 2000, {leading: true, trailing: false})
return <div>
<div><button onClick={onClick}>Click</button></div>
{msg}
</div>
}
function Parent() {
const [count, setCount] = useState(0);
useEffect(() => {
setInterval(() => setCount(x => x+1), 500)
}, [])
return (
<div>
<div>The count is {count}</div>
<Reporter count={count} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Parent />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<div id="root" />
I used useCallback with _.debounce, but faced with a eslint error, 'React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead.'
Finally, I found this issue, and used useMemo.
before:
const debouncedMethod = useCallback(
debounce((arg) => {
someMethod(arg);
}, 500),
[someMethod],
);
after:
const debouncedMethod = useMemo(
() =>
debounce((arg) => {
someMethod(arg);
}, 500),
[someMethod],
);
lodash.debounce creates a new function invocations of which will be debounced. Use case scenario is creating it once, storing result in a variable and calling it multiple times so the calls get debounced. You are creating a new debounced function every time you render a component so every new render you get a new debounce scope
You want to call const debouncedChange = _.debounce(...) only once per component instance. I am not very much familiar with hooks, but guess you can do this
const [debouncedChange] = React.useState(_.debounce(
(field, payload) => handleChangeAt(field, payload),
500,
))
this will create your function once during render and reuse what's created across successive renders

Should I include setState in useCallback's array of dependencies?

const [active, setActive] = useState(false);
const onActiveChanged = useCallback(
isActive => () => {
// do something
setActive(isActive);
},
[setActive], // or just [] is okay?
);
When using useState and useCallback (or useMemo) together, should I include setState in the array of dependencies?
The recommendation for that is also on React Docs - Hooks API Reference.
The setState function is used to update the state. It accepts a new
state value and enqueues a re-render of the component.
setState(newState);
During subsequent re-renders, the first value
returned by useState will always be the most recent state after
applying updates.
Note
React guarantees that setState function identity is stable and won’t
change on re-renders. This is why it’s safe to omit from the useEffect
or useCallback dependency list.
The purpose of useCallback as you rightly hinted is to memoise:
useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).
And as for what useMemo is intended for:
You may rely on useMemo as a performance optimization, not as a semantic guarantee.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
But as a rule, useState is stable between renders (i.e. pre-memoised), so you should not need to memoise it again.
The question then comes, is your 'do something' below an expensive calculation? It shouldn't be to onerous to make use of useCallback, but it could well be boilerplate code you don't need, and could make almost direct use of your setActive function.
const [active, setActive] = useState(false);
const onActiveChanged = useCallback(
isActive => () => {
// do something
setActive(isActive);
},
[setActive], // or just [] is okay?
);
Another way to prevent unnecessary dependencies, in your useCallback and other hooks, is to make use of functional updates. The result being that you can have these:
const [active, setActive] = useState(false);
const [expensiveCalc, setExpensiveCalc] = useState(false);
const onExpensiveCalc = useCallback(
expensiveInput => () => {
const newState = doExpensiveCalc(expensiveInput);
expensiveCalc(newState);
},
[setActive], // here for completeness
);
return (<>
// expensive calculation
<button onClick={onExpensiveCalc}>Do lengthy calculation</button>
// cheap calculation, using functional updates
<button onClick={() => setActive(prevBoolean => !prevBoolean)}>Cheap Set Active</button>
</>)
Do note, that there's a little nuance to how set state works in an onClick, and you should make use of an arrow function, so your setActive is run on click, not render. This is shown in the second answer above, but without explanation.
See also: What is useState() in React?

Using useMemo instead of React.memo syntax issue

I need to make a demonstration of using React Hooks useMemo. I have working code that is as follows that does what I want:
const SpeakerCardDetail = React.memo(
({id,...
I found a link that shows that I could use syntax more like this but I can't figure it out exactly.
This is as far as I got:
const SpeakerDetail = React.useMemo(() => {
({ id,
Clearly not it though. I do get that React.memo solves the problem but I do need to show useMemo in action and am hoping there is an alternative syntax that I can use.
React.memo and React.useMemo are not equivalent at all (don't rely on naming similarity). Here's a quote from React.memo doc:
React.memo is a higher order component.
So it's a HOC that can optimize rendition of your component given that it renders the same output with the same properties.
React.useMemo on the other hand is more generic and returns a memoized value:
Pass a “create” function and an array of dependencies. useMemo will
only recompute the memoized value when one of the dependencies (either a or b) has
changed.
const memoizedValue = useMemo(
() => computeExpensiveValue(a, b),
[a, b]
);
And while it can be hacked to be used instead of React.memo, it's not its purpose and it will add to the confusion more than it will help. useMemo is a hook and is subject to the certain usage rules.
And there's this warning as well:
In the future, React may choose to “forget” some previously memoized
values and recalculate them on next render, e.g. to free memory for
offscreen components. Write your code so that it still works without
useMemo — and then add it to optimize performance.
While memo is a HOC and useMemo is a hook, you can use them the achieve the same result.
For context, HOC is an older React pattern that has been used for many years with class-based and functional components alike. You can still use it today (there's no plan for deprecation).
Hooks is a relatively new concept (about a year) that enhances functional components and in many cases drastically simplifies code. That's why many developers are moving towards using hooks.
Anyway, both memo and useMemo take two arguments: a function and props. If none of the props change on subsequent re-renders, the function is not executed again and instead returns the previous result. This, in effect, replaces shouldComponentUpdate callbacks, with a purely functional approach.
With memo, your code would look like this:
const SpeakerCardDetail = React.memo(
(props) => <div>{props.name}</div>
)
With useMemo, you'd write:
const SpeakerCardDetail = (props) => useMemo(() => <div>{props.name}</div>)
Notice that useMemo is used inside of your component function, while memo wraps the function.
More traditionally, useMemo could be written as:
function SpeakerCardDetail(props) {
return useMemo(
() => <div>{props.name}</div>
)
}
Now, the code above would re-render every time, making the useMemo function a bit useless. To make it work its magic, we need to add the second argument. (memo still works even without specifying the second argument but you can add it to customize it)
There's a slight difference in the format of the second argument. memo expects a function that compares previous and current props, just like shouldComponentUpdate does for class components.
const SpeakerCardDetail = React.memo(
(props) => <div>{props.name}</div>
,
// return true if passing nextProps to render would return the same result as passing prevProps to render, otherwise return false
(prevProps, nextProps) => prevProps.name === nextProps.name
)
useMemo, on the other hand, expects an array as the second argument. Whenever the values in the array change, the function would be executed again.
function SpeakerCardDetail(props) {
return useMemo(
() => <div>{props.name}</div>
,
[props.name]
)
}
There's really no more magic than that. Both memo and useMemo are used to memoize the result of a function, the only difference is memo is a HOC (and can be used to wrap both class and functional components) which useMemo is a hook (and can only be used inside functional components).
To summarise React.memo vs useMemo / TLDR:
React.memo is a higher-order component (HOC for short) that will memoize a react component based on the props.
export function SomeComponent({ num }) {
return <p>{num * 10}</p>
}
export default React.memo(SomeComponent, function areEqual(
prevProps,
nextProps
) {
if (prevProps.num !== nextProps.num) {
return false
}
return true
})
useMemo is a react hook that will memoize the value that is returned from the function you provide it.
export function SomeComponent({ num }) {
const res = useMemo(() => num * 10, [num])
return <p>{res}</p>
}
Source
React.Memo
Using React.memo will cause React to skip rendering a component if its props have not changed.
Example:
const Child = React.memo(props => {
console.log("rendered");
return <React.Fragment>{props.name}</React.Fragment>;
});
class App extends React.Component {
state = {
value: 1,
name: "Jioke"
};
handleClick = () => {
this.setState({
value: this.state.value + 1
});
};
render() {
return (
<React.Fragment>
<Child name={this.state.name} />
<div>{this.state.value}</div>
<button onClick={this.handleClick}>+</button>
</React.Fragment>
);
}
}
When we click button, Child Component does not re-render(rendered is displayed only 1)
Notes: The more props, the more calculations, comparison(check is render or not) added comparison cost is not worth it for a "simple" component in terms of render, reconcile, DOM change and side-effect costs. So be careful to decide use it or not
UseMemo
useMemo will cache a value so that it does not need to be recalculated each times components is re-render. It saves return value of function and returns if the inputs are not changed.
Example:
import { useState, useMemo } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState([]);
const calculation = useMemo(() => expensiveCalculation(count), [count]);
const increment = () => {
setCount((c) => c + 1);
};
const addTodo = () => {
setTodos((t) => [...t, "New Todo"]);
};
return (
<div>
<div>
<h2>My Todos</h2>
{todos.map((todo, index) => {
return <p key={index}>{todo}</p>;
})}
<button onClick={addTodo}>Add Todo</button>
</div>
<hr />
<div>
Count: {count}
<button onClick={increment}>+</button>
<h2>Expensive Calculation</h2>
{calculation}
</div>
</div>
);
};
const expensiveCalculation = (num) => {
console.log("Calculating...");
for (let i = 0; i < 1000000000; i++) {
num += 1;
}
return num;
};
ReactDOM.render(<App />, document.getElementById('root'));
we have an expensive function that runs on every render.
When changing the count or adding a todo, you will notice a delay in execution.
So when we use useMemo the expensive function will only run when its dependencies have changed.
In the following example, the expensive function will only run when count is changed and not when todo's are added.
Please use the below simple example to better understand the answers above. The example demonstrates these scenarios in the simplest way:
memo
If the props do not change, the component won't be re-rendered.
useMemo
If the dependencies do not change, the component won't be re-rendered.
If the dependencies do not change, the function (useful for expensive functions) won't be re-run.
// index.js
import React , { useState, useMemo } from 'react';
import ReactDOM from 'react-dom/client';
const Child1 = (props) => {
console.log("Child1");
return <p>Child 1</p>;
};
const Child2 = React.memo((props) => {
console.log("Child2");
return <p>Child 2</p>;
});
const Child3 = (props) => {
console.log("Child3");
return <p>Child 3</p>;
};
const expensiveCalculation = (label) => {
console.log(label);
return label;
};
function App() {
console.log("App");
const [count, setCount] = useState(0);
const child3 = useMemo(() => <Child3 />, []);
const calculation1 = expensiveCalculation("Expensive calculation without useMemo");
const calculation2 = useMemo(() => expensiveCalculation("Expensive calculation with useMemo"), []);
return (
<>
<button onClick={() => {setCount(c => c + 1);}}>Increase count</button>
<p>Current count: {count}</p>
<Child1 />
<Child2 />
{child3}
<p>{calculation1}</p>
<p>{calculation2}</p>
</>
);
};
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
Input/output:
// Initial render
App
Expensive calculation without useMemo
Expensive calculation with useMemo
Child1
Child2
Child3
// Click on the "Increase count" button and observe the impact of memo and useMemo.
App
Expensive calculation without useMemo
Child1
Notes:
I have taken a very simple example to make it easy to explain. Please research a little bit more on the arguments memo and useMemo takes.

Resources