Using multiple setHook makes component render more than once? - reactjs

If I have alot of useState and on the click of a button I call multiple setHook, does it renders multiple times or only once?
e.g.
export default function setMultipleHooks() {
const [hook1, setHook1] = useState(false)
const [hook2, setHook2] = useState(false)
const [hook3, setHook3] = useState(false)
const [hook4, setHook4] = useState(false)
const setHooks = () => {
setHook1(true)
setHook2(true)
setHook3(true)
setHook4(true)
}
return (
<button onClick={setHooks} >Hey</button>
)
}
Once I click the button, how many times does it render? 1 or 4 times?
Normally to check how many times the component is rendering, I just put a console.log in the render method, but with functional components, I'm not sure how to test this.
If it renders 4 times, it would be better to use only one useState (passing an object) if the 4 hooks are always related?

React may batch multiple setState calls and render them all at once. And in your case it do that, because you're inside an event handler (onClick).
It renders only once (initial render not included) as you can see from the snippet below.
Github issue about React batching setState() calls
You can, nevertheless use an object with 4 properties instead of 4 state hook varibales. But you'll have to be careful when updating state, because hook's setState() doesn't auto merge your last state with your new state property. It completely replaces the state with the new object you're passing.
React Docs - Using the state hook
So you'd have to do something like this:
function YourComponent() {
const INITIAL_STATE = {a: false, b: false, c: false, d: false};
const [state,setState] = useState(INITIAL_STATE);
function handleClick() {
setState((prevState) => {
return({
...prevState,
b: true
});
});
}
}
const { useState, useRef, useEffect } = React;
function App() {
const renderTimes = useRef(0);
const [hook1, setHook1] = useState(false);
const [hook2, setHook2] = useState(false);
const [hook3, setHook3] = useState(false);
const [hook4, setHook4] = useState(false);
const setHooks = () => {
setHook1(true);
setHook2(true);
setHook3(true);
setHook4(true);
}
useEffect( () => {
renderTimes.current+=1;
});
return (
<div>
<div>I rendered {renderTimes.current} time(s).</div>
<div>NOTE: The initial render is not included.</div>
<div>State hook1 is equal to: {hook1.toString()}</div>
<div>State hook2 is equal to: {hook2.toString()}</div>
<div>State hook3 is equal to: {hook3.toString()}</div>
<div>State hook4 is equal to: {hook4.toString()}</div>
<button onClick={setHooks}>Click</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

Normally to check how many times the component is rendering, I just put a console.log in the render method, but with functional components, I'm not sure how to test this.
Just put the console.log inside function (function itself is render method).
It will render only once, because there is a small time interval within which if multiple calls to useState setter occur their updates will be batched.
If it renders 4 times, it would be better to use only one useState (passing an object) if the 4 hooks are always related?
If you have complex state you should really consider useReducer.
More:
https://stackoverflow.com/a/54716601/10995369
https://github.com/acdlite/react-fiber-architecture

Related

Why is my boolean state value not toggling?

I know there are other articles and posts on this topic and almost all of them say to use the ! operator for a Boolean state value. I have used this method before but for the life of me I can not toggle this Boolean value.
import { useState } from 'react';
const [playerTurn, setPlayerTurn] = useState(true);
const changePlayerTurn = () => {
console.log(playerTurn); // returns true
setPlayerTurn(!playerTurn);
console.log(playerTurn); // also returns true
};
changePlayerTurn();
I have also tried setPlayerTurn(current => !current), commenting out the rest of my code to avoid interference, and restarted my computer in case that would help but I am still stuck with this issue.
Can anyone point out why this is not working?
The setPlayerTurn method queues your state change (async) so reading the state directly after will provide inconsistent results.
If you use your code correctly in a react component you will see that playerTurn has changed on the next render
You creating a async function, to solve this you can create a button in your component, which will run the function and you can use the "useEffect" hook to log every time the boolean changes... so you can see the changes taking place over time, like this:
import React, { useEffect } from "react";
import { useState } from "react";
const Player = () => {
const [playerTurn, setPlayerTurn] = useState(true);
useEffect(() => {
console.log(playerTurn);
}, [playerTurn]);
return <button onClick={() => setPlayerTurn(!playerTurn)}>change player turn</button>;
};
export default Player;
This is happening because setPlayerTurn is async function.
You can use another hook useEffect() that runs anytime some dependencies update, in this case your playerTurn state.
export default YourComponent = () => {
const [playerTurn, setPlayerTurn] = useState(true);
useEffect(() => {
console.log('playerTurn: ', playerTurn);
}, [playerTurn]);
const changePlayerTurn = () => {
setPlayerTurn(!playerTurn);
}
return (
<button onClick={changePlayerTurn}>Click to change player turn</button>
);
}
Basically whenever you use setState React keeps a record that it needs to update the state. And it will do some time in the future (usually it takes milliseconds). If you console.log() right after updating your state, your state has yet to be updated by React.
So you need to "listen" to changes on your state using useEffect().
useEffect() will run when your component is first mounted, and any time the state in the dependencies array is updated.
The value of the state only changes after the render. You can test this like:
// Get a hook function
const Example = ({title}) => {
const [playerTurn, setPlayerTurn] = React.useState(true);
React.useEffect(() => {
console.log("PlayerTurn changed to", playerTurn);
}, [playerTurn]);
console.log("Rendering...")
return (<div>
<p>Player turn: {playerTurn.toString()}</p>
<button onClick={() => setPlayerTurn(!playerTurn)}>Toggle PlayerTurn</button>
</div>);
};
// Render it
ReactDOM.render(
<Example />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
The callback inside the useEffect runs during the component mount and when one of the values inside the second argument, the dependecy array, changes. The depency here is playerTurn. When it changes the console will log.
As you will see, before this happens, the "Rendering..." log will appear.

Best practices for callback functions [duplicate]

What is the main difference between useCallback, useMemo and useEffect?
Give examples of when to use each of them.
A short explanation.
useEffect
It's the alternative for the class component lifecycle methods componentDidMount, componentWillUnmount, componentDidUpdate, etc. You can also use it to create a side effect when dependencies change, i.e. "If some variable changes, do this".
useCallback
On every render, everything that's inside a functional component will run again. If a child component has a dependency on a function from the parent component, the child will re-render every time the parent re-renders even if that function "doesn't change" (the reference changes, but what the function does won't).
It's used for optimization by avoiding unnecessary renders from the child, making the function change the reference only when dependencies change.
You should use it when a function is a dependency of a side effect e.g. useEffect.
useMemo
It will run on every render, but with cached values. It will only use new values when certain dependencies change. It's used for optimization when you have expensive computations. Here is also a good answer that explains it.
useEffect() will let you create side effects on your components based on the dependencies you send to it.
function Example() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'))
<script src="https://unpkg.com/react#16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.0/umd/react-dom.development.js"></script>
<div id="root"></div>
The example above is taken from the documentation of React. You can see that each time you click the button it will trigger an update on the count field (using setCount()) and then, the effect that depends on the count variable will trigger an update on the title of the page.
useCallback() will return a memoized callback. Normally, if you have a child component that receives a function prop, at each re-render of the parent component, this function will be re-executed; by using useCallback() you ensure that this function is only re-executed when any value on it's dependency array changes.
function ExampleChild({ callbackFunction }) {
const [value, setValue] = React.useState(0);
React.useEffect(() => {
setValue(value + 1)
}, [callbackFunction]);
return (<p>Child: {value}</p>);
}
function ExampleParent() {
const [count, setCount] = React.useState(0);
const [another, setAnother] = React.useState(0);
const countCallback = React.useCallback(() => {
return count;
}, [count]);
return (
<div>
<ExampleChild callbackFunction={countCallback} />
<button onClick={() => setCount(count + 1)}>
Change callback
</button>
<button onClick={() => setAnother(another + 1)}>
Do not change callback
</button>
</div>
)
}
ReactDOM.render(<ExampleParent />, document.getElementById('root'));
<script src="https://unpkg.com/react#16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.0/umd/react-dom.development.js"></script>
<div id="root"></div>
useMemo() will return a memoized value that is the result of the passed parameter. It means that useMemo() will make the calculation for some parameter once and it will then return the same result for the same parameter from a cache.
This is very useful when you need to process a huge amount of data.
function ExampleChild({ value }) {
const [childValue, setChildValue] = React.useState(0);
React.useEffect(() => {
setChildValue(childValue + 1);
}, [value])
return <p>Child value: {childValue}</p>;
}
function ExampleParent() {
const [value, setValue] = React.useState(0);
const heavyProcessing = () => {
// Do some heavy processing with the parameter
console.log(`Cached memo: ${value}`);
return value;
};
const memoizedResult = React.useMemo(heavyProcessing, [value]);
return (
<div>
<ExampleChild value={memoizedResult} />
<button onClick={() => setValue(value + 1)}>
Change memo
</button>
</div>
)
}
ReactDOM.render(<ExampleParent />, document.getElementById('root'));
<script src="https://unpkg.com/react#16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.0/umd/react-dom.development.js"></script>
<div id="root"></div>
Most minimal explanation:
useEffect:
Whenever you have some logic that is executed as reaction to a state change or before a change is about to happen.
useEffect(() => {
// execute when state changed
() => {
// execute before state is changed
}
}, [state]);
or in case of no dependency:
useEffect(() => {
// execute when component has mounted
() => {
// execute when component will unmount
}
}, []);
useCallback:
Whenever you have a function that is depending on certain states. This hook is for performance optimization and prevents a function inside your component to be reassigned unless the depending state is changed.
const myFunction = useCallback(() => {
// execute your logic for myFunction
}, [state]);
Without useCallback, myFunction will be reassigned on every render. Therefore it uses more compute time as it would with useCallback.
useMemo
Whenever you have a value that is depending on certain state. Same as useCallback, useMemo is ment to reduce reassignments for performance optimization.
const myValue = useMemo(() => {
// return calculated value
}, [state]);
Same as useCallback, myValue is only assigned when state is changing and therefore will reduce compute time. Otherwise myValue will be reassigned on every render.
!Trick to mimick componentWillMount lifecycle
useMemo(() => {
// execute componentWillMount logic
]}, []);
Since useEffect is called after the first render and then on every dependency change. It never runs before the first render.
useMemo is executed inline with your JS therefore will be executed before it reaches your Components return statement.
!NOTE: functions with useCallback and values with useMemo can be used as dependency in useCallback, useMemo and useEffect. It is highly recommended to use these hooks in order to have a well structured and readable flow of state in your component. These hooks do not trigger a render. Only useState and useReducer do!
If you want to keep state that doesnt trigger a rerender or any of the above explained hooks you can use useRef. useRef will keep a value consistent over renders without triggering any state dependent value or effect.
It's all well and good to know when to use the functions, but I wanted to know what the actual difference was between them! Here is what I found:
useMemo runs the code immediately, so the return value is available to code that comes after after it. This means it runs before the first render, so any useRef you are using to access HTML components won't be available on the initial run. (But you can add ref.current to the useMemo dependencies to have the useMemo code run again after the first render, when the useRef value has become available). Since the return value is available to code directly following it, this is why it is recommended for complex calculations that don't need to re-run on each render, as having the return value available immediately saves you from having to mess with the state to store the value now and access it later - just grab the return value of useMemo() and use it right away.
useEffect does not run immediately but runs after the first render. This means any useRef values referring to HTML elements will be valid on the first run. Since it runs after all the code in your function has finished and rendered, there is no point having a return value as there is no further code running that could use it. The only way useEffect code can do anything visible is by either changing the state to cause a re-render, or modifying the DOM directly.
useCallback is the same as useMemo except that it remembers the function itself rather than its return value. This means a useCallback function does not run immediately but can be run later (or not run at all), while useMemo runs its function immediately and just saves its return value for later use. Unlike useMemo this is not good for complex calculations as the code will run again every time it is used. If you ever pass a callback function as a prop to another component in your render function, make sure you're passing the return value of useCallback. If you make your callback function like const onClick = () => { ... } or in JSX as onClick={() => { ... }} then every render you will get a new instance of the function, so the child component will always re-render as it thinks you're changing the callback to a different function on every single render. But useCallback returns the same instance of the function each time, so the child function may skip the render completely if nothing else changed, making your app more responsive.
For example, if we pass the same function to both useMemo and useCallback:
let input = 123;
const output = useMemo(() => {
return input + 1;
}, [
input,
]);
// The above function has now run and its return value is available.
console.log( output ); // 124
input = 125; // no effect as the function has already run
console.log( output ); // 124
let input = 123;
const output = useCallback(() => {
return input + 1;
}, [
input,
]);
// The above function has not run yet but we can run it now.
console.log( output() ); // 124
input = 125; // changes the result as the function is running again
console.log( output() ); // 126
Here, useCallback has remembered the function and will keep returning the original function on future renders until the dependencies change, while useMemo actually runs the function immediately and just remembers its return value.
Both useCallback() and useMemo() provide return values that can be used immediately, while useEffect() does not because its code does not run until much later, after the render has completed.
useEffect
Gets called when the component mounts, unmounts and any of it's dependencies change.
Can be used to get data when component is mounted, subscribe and unsubscribe to event streams when component mounts and unmounts (think rxjs).
const [userId, updateUser] = useState(1);
useEffect(()=>{
//subscription
const sub = getUser(userId).subscribe(user => user);
// cleanup
return () => {
sub.unsubscribe();
}
},[userId]) // <-- Will get called again when userId changes
Can also be used for onetime method call which require no cleanup
useEffect(()=>{
oneTimeData();
},[]); // pass empty array to prevent being called multiple times
useCallback
Got functions that you don't want to be re-created on every component render?
Want a function that isn't called on component mount or unmount?
Use useCallback
const [val, updateValue] = useState(0);
const Compo = () => {
/* inc and dec will be re-created on every component render.
Not desirable a function does very intensive work.
*/
const inc = () => updateValue(x => x + 1);
const dec = () => updateValue(x => x - 1);
return render() {
<Comp1 onClick={inc} />
<Comp2 onClick={dec} />
}
}
useCallback to the rescue
const [val, updateValue] = useState(0);
const Compo = () => {
const callbackInc = useCallback(() => {
setCount(currentVal => currentVal + 1);
}, []);
const callbackDec = useCallback(() => {
setCount(currentVal => currentVal - 1);
}, []);
return render() {
<Comp1 onClick={callbackInc} />
<Comp2 onClick={callbackDec} />
}
}
If the argument passed to setCount isn't a function, then the variables you would want useCallback to 'watch' out for must be specified in the dependencies array less there will be no change effect.
const callbackInc = useCallback(() => {
setCount(val + 1); // val is an 'outside' variable therefore must be specified as a dependency
}, [val]);
useMemo
Doing heavy processing and want to memoize (cache) the results? Use useMemo
/*
heavyProcessFunc will only be called again when either val or val2 changes
*/
const result = useMemo(heavyProcessFunc(val, val2),[val,val2])
All of these hooks have the same goal: avoiding redundant component rebuilds (and re-execution of the stuff inside the hooks).
useEffect returns nothing (void) and thus is suitable for such cases.
useCallback returns a function which will be used later in the component. Unlike normal function declaration, it will not trigger component rebuild unless its dependencies change.
useMemo is just another flavour of useCallback.
Here is the best explanation I've seen so far.

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

When to use useCallback, useMemo and useEffect?

What is the main difference between useCallback, useMemo and useEffect?
Give examples of when to use each of them.
A short explanation.
useEffect
It's the alternative for the class component lifecycle methods componentDidMount, componentWillUnmount, componentDidUpdate, etc. You can also use it to create a side effect when dependencies change, i.e. "If some variable changes, do this".
useCallback
On every render, everything that's inside a functional component will run again. If a child component has a dependency on a function from the parent component, the child will re-render every time the parent re-renders even if that function "doesn't change" (the reference changes, but what the function does won't).
It's used for optimization by avoiding unnecessary renders from the child, making the function change the reference only when dependencies change.
You should use it when a function is a dependency of a side effect e.g. useEffect.
useMemo
It will run on every render, but with cached values. It will only use new values when certain dependencies change. It's used for optimization when you have expensive computations. Here is also a good answer that explains it.
useEffect() will let you create side effects on your components based on the dependencies you send to it.
function Example() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'))
<script src="https://unpkg.com/react#16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.0/umd/react-dom.development.js"></script>
<div id="root"></div>
The example above is taken from the documentation of React. You can see that each time you click the button it will trigger an update on the count field (using setCount()) and then, the effect that depends on the count variable will trigger an update on the title of the page.
useCallback() will return a memoized callback. Normally, if you have a child component that receives a function prop, at each re-render of the parent component, this function will be re-executed; by using useCallback() you ensure that this function is only re-executed when any value on it's dependency array changes.
function ExampleChild({ callbackFunction }) {
const [value, setValue] = React.useState(0);
React.useEffect(() => {
setValue(value + 1)
}, [callbackFunction]);
return (<p>Child: {value}</p>);
}
function ExampleParent() {
const [count, setCount] = React.useState(0);
const [another, setAnother] = React.useState(0);
const countCallback = React.useCallback(() => {
return count;
}, [count]);
return (
<div>
<ExampleChild callbackFunction={countCallback} />
<button onClick={() => setCount(count + 1)}>
Change callback
</button>
<button onClick={() => setAnother(another + 1)}>
Do not change callback
</button>
</div>
)
}
ReactDOM.render(<ExampleParent />, document.getElementById('root'));
<script src="https://unpkg.com/react#16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.0/umd/react-dom.development.js"></script>
<div id="root"></div>
useMemo() will return a memoized value that is the result of the passed parameter. It means that useMemo() will make the calculation for some parameter once and it will then return the same result for the same parameter from a cache.
This is very useful when you need to process a huge amount of data.
function ExampleChild({ value }) {
const [childValue, setChildValue] = React.useState(0);
React.useEffect(() => {
setChildValue(childValue + 1);
}, [value])
return <p>Child value: {childValue}</p>;
}
function ExampleParent() {
const [value, setValue] = React.useState(0);
const heavyProcessing = () => {
// Do some heavy processing with the parameter
console.log(`Cached memo: ${value}`);
return value;
};
const memoizedResult = React.useMemo(heavyProcessing, [value]);
return (
<div>
<ExampleChild value={memoizedResult} />
<button onClick={() => setValue(value + 1)}>
Change memo
</button>
</div>
)
}
ReactDOM.render(<ExampleParent />, document.getElementById('root'));
<script src="https://unpkg.com/react#16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.0/umd/react-dom.development.js"></script>
<div id="root"></div>
Most minimal explanation:
useEffect:
Whenever you have some logic that is executed as reaction to a state change or before a change is about to happen.
useEffect(() => {
// execute when state changed
() => {
// execute before state is changed
}
}, [state]);
or in case of no dependency:
useEffect(() => {
// execute when component has mounted
() => {
// execute when component will unmount
}
}, []);
useCallback:
Whenever you have a function that is depending on certain states. This hook is for performance optimization and prevents a function inside your component to be reassigned unless the depending state is changed.
const myFunction = useCallback(() => {
// execute your logic for myFunction
}, [state]);
Without useCallback, myFunction will be reassigned on every render. Therefore it uses more compute time as it would with useCallback.
useMemo
Whenever you have a value that is depending on certain state. Same as useCallback, useMemo is ment to reduce reassignments for performance optimization.
const myValue = useMemo(() => {
// return calculated value
}, [state]);
Same as useCallback, myValue is only assigned when state is changing and therefore will reduce compute time. Otherwise myValue will be reassigned on every render.
!Trick to mimick componentWillMount lifecycle
useMemo(() => {
// execute componentWillMount logic
]}, []);
Since useEffect is called after the first render and then on every dependency change. It never runs before the first render.
useMemo is executed inline with your JS therefore will be executed before it reaches your Components return statement.
!NOTE: functions with useCallback and values with useMemo can be used as dependency in useCallback, useMemo and useEffect. It is highly recommended to use these hooks in order to have a well structured and readable flow of state in your component. These hooks do not trigger a render. Only useState and useReducer do!
If you want to keep state that doesnt trigger a rerender or any of the above explained hooks you can use useRef. useRef will keep a value consistent over renders without triggering any state dependent value or effect.
It's all well and good to know when to use the functions, but I wanted to know what the actual difference was between them! Here is what I found:
useMemo runs the code immediately, so the return value is available to code that comes after after it. This means it runs before the first render, so any useRef you are using to access HTML components won't be available on the initial run. (But you can add ref.current to the useMemo dependencies to have the useMemo code run again after the first render, when the useRef value has become available). Since the return value is available to code directly following it, this is why it is recommended for complex calculations that don't need to re-run on each render, as having the return value available immediately saves you from having to mess with the state to store the value now and access it later - just grab the return value of useMemo() and use it right away.
useEffect does not run immediately but runs after the first render. This means any useRef values referring to HTML elements will be valid on the first run. Since it runs after all the code in your function has finished and rendered, there is no point having a return value as there is no further code running that could use it. The only way useEffect code can do anything visible is by either changing the state to cause a re-render, or modifying the DOM directly.
useCallback is the same as useMemo except that it remembers the function itself rather than its return value. This means a useCallback function does not run immediately but can be run later (or not run at all), while useMemo runs its function immediately and just saves its return value for later use. Unlike useMemo this is not good for complex calculations as the code will run again every time it is used. If you ever pass a callback function as a prop to another component in your render function, make sure you're passing the return value of useCallback. If you make your callback function like const onClick = () => { ... } or in JSX as onClick={() => { ... }} then every render you will get a new instance of the function, so the child component will always re-render as it thinks you're changing the callback to a different function on every single render. But useCallback returns the same instance of the function each time, so the child function may skip the render completely if nothing else changed, making your app more responsive.
For example, if we pass the same function to both useMemo and useCallback:
let input = 123;
const output = useMemo(() => {
return input + 1;
}, [
input,
]);
// The above function has now run and its return value is available.
console.log( output ); // 124
input = 125; // no effect as the function has already run
console.log( output ); // 124
let input = 123;
const output = useCallback(() => {
return input + 1;
}, [
input,
]);
// The above function has not run yet but we can run it now.
console.log( output() ); // 124
input = 125; // changes the result as the function is running again
console.log( output() ); // 126
Here, useCallback has remembered the function and will keep returning the original function on future renders until the dependencies change, while useMemo actually runs the function immediately and just remembers its return value.
Both useCallback() and useMemo() provide return values that can be used immediately, while useEffect() does not because its code does not run until much later, after the render has completed.
useEffect
Gets called when the component mounts, unmounts and any of it's dependencies change.
Can be used to get data when component is mounted, subscribe and unsubscribe to event streams when component mounts and unmounts (think rxjs).
const [userId, updateUser] = useState(1);
useEffect(()=>{
//subscription
const sub = getUser(userId).subscribe(user => user);
// cleanup
return () => {
sub.unsubscribe();
}
},[userId]) // <-- Will get called again when userId changes
Can also be used for onetime method call which require no cleanup
useEffect(()=>{
oneTimeData();
},[]); // pass empty array to prevent being called multiple times
useCallback
Got functions that you don't want to be re-created on every component render?
Want a function that isn't called on component mount or unmount?
Use useCallback
const [val, updateValue] = useState(0);
const Compo = () => {
/* inc and dec will be re-created on every component render.
Not desirable a function does very intensive work.
*/
const inc = () => updateValue(x => x + 1);
const dec = () => updateValue(x => x - 1);
return render() {
<Comp1 onClick={inc} />
<Comp2 onClick={dec} />
}
}
useCallback to the rescue
const [val, updateValue] = useState(0);
const Compo = () => {
const callbackInc = useCallback(() => {
setCount(currentVal => currentVal + 1);
}, []);
const callbackDec = useCallback(() => {
setCount(currentVal => currentVal - 1);
}, []);
return render() {
<Comp1 onClick={callbackInc} />
<Comp2 onClick={callbackDec} />
}
}
If the argument passed to setCount isn't a function, then the variables you would want useCallback to 'watch' out for must be specified in the dependencies array less there will be no change effect.
const callbackInc = useCallback(() => {
setCount(val + 1); // val is an 'outside' variable therefore must be specified as a dependency
}, [val]);
useMemo
Doing heavy processing and want to memoize (cache) the results? Use useMemo
/*
heavyProcessFunc will only be called again when either val or val2 changes
*/
const result = useMemo(heavyProcessFunc(val, val2),[val,val2])
All of these hooks have the same goal: avoiding redundant component rebuilds (and re-execution of the stuff inside the hooks).
useEffect returns nothing (void) and thus is suitable for such cases.
useCallback returns a function which will be used later in the component. Unlike normal function declaration, it will not trigger component rebuild unless its dependencies change.
useMemo is just another flavour of useCallback.
Here is the best explanation I've seen so far.

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