virtual DOM : ReactDOM receives the differences(React only passes the changes) and then manipulates the real DOM
the component where you manage state or where you have props or context, and if those changes, that component will be re-evaluated and re-executed
if the parent component is re-evaluated, all its child components are also re-evaluated
=> this is unnecessary re-evaluation
=> how to optimize?
DemoOutput.js
const DemoOutput = (props) => {
console.log('DemoOutput Running');
return <MyParagraph>
{props.show ? 'This is new!' : ''}
</MyParagraph>
}
export default ✨React.memo(DemoOutput)
React should look at the props that this component gets
and check the new value for all those props
and compare it to the previous value
only if the value of a prop changed
the component should be re-executed and re-evaluated
if the value of a prop doesn't change,
the child component (MyParagraph) isn't re-executed also
We don't use it to all components because of its costs
=> can be a good choice to use...
if you have a huge component tree with a lot of child components
and on a high level in the component tree,
you can avoid unnecessary re-render cycles
for the entire branch of the component tree
=> would not be a good choice to use...
if it is for smaller apps
if you have a component where you know it's going to change or its props values are going to change with pretty much every re-evaluation of the parent component
because the component should re-render anyways,
you can save that extra comparison of the prop values
stores a function(array/object) when the component re-evaluates
App.js
import React, { useState, useCallback } from 'react';
// if the state chagnes, function App re-executes
// toggleParagraphHandler also regeneragtes in every execution
const App = () => {
const [showParagraph, setShowParagraph] = useState(false);
const [allowToggle, setAllowToggle] = useState(false);
const toggleParagraphHandler = ✨useCallback(() => {
if (allowToggle) {
setShowParagraph((prevShowParagraph) => !prevShowParagraph)
}
}, [allowToggle]);
const allowToggleHandler = () => {
setAllowToggle(true);
}
// ⭐ closures ⭐
// functions in js are closures
// when a function is defined
// , which happens when the app function runs
// javascript basically locks in(stores) all the variables
// that are defined outside of the function that we are using
// (in this code...)
// when the App component is re-evaluated,
// react will not recreate the function in useCallback()
// therefore the allowToggle function is still the old one
// , not the most recent one
// so by putting allowToggle as dependencies,
// we recreate a function in useCallback whenever allowToggle changes
// because values being used in that function
// that are coming from outside the function might have changed
return(
<Button onClick = {allowToggleHandler}>
Allow Toggling
</Button>
<Button onClick = {toggleParagraphHandler}>
Toggle Paragraph!
</Button>
}
Button.js
// since function is not a primitive type, but a reference type,
// React(javascript) regards the regenerated function
// as an unidentical one
// so when comparing previous function(toggleParagraphHandler)
// and an updated function(toggleParagraphHandler)
// it makes React.memo() useless
// ✨-> use useCallback()! for reference type(function)
export default ⭐ React.memo(Button)
React creates a new state variable using the default value then memorizes to which component that belongs
the default value that is passed in useState essentially is only considered once, the first time the component is rendered
So when the component is re-evaluated, no useState is created
Instead React recognizes that it already has a state for the very component, and it simply updates that state as needed
Therefore React will only do state managing and updating, no reinitialize, unless the component was totally removed
(a very much instant process)
before changing the actual state,
React schedules the state change
(postpones the state change)
because when a lot of other performance intensive tasks are going on at the same moment, react considers to have a higher priority for that performance
(ex. you have an input field where the user is able to type something)
this guarantees the state changing to process ordlerly
but because of scheduling,
state change is not necessarily executed immediately
since multipe scheduled state can change at the same time,
(multiple updates can be scheduled at the same time)
it is recommended that you use function form below
// if you depend on the previous state snapshot
// for updating the state...
// this can overcome the delay (though it would proceed very quickly)
// and ensures that you depend on the latest state
set~ ((prevState) => ...)
useEffect(()=> {
const identifier = setTimeout(() => {
setFormIsValid(
emailIsValid && passwordIsValid
)
}
, 500);
return () => {
console.log('clean-up');
clearTimeout(identifier);
};
}, [emailIsValid, passwordIsValid]);
const navigateHandler = (navPath) => {
setCurrentNavPath(navPath);
setDrawerIsOpen(false);
}
in this case, the component isn't re-evaluated twice
instead, if you have two state updates,
react will batch(bundle) these state updating together
App.js
import React, {useState, useCallback, ⭐ useMemo} from 'react';
function App() {
const [listTitle, setListTitle] = useState('My List');
const changeTitleHandler = useCallback(() => {
setListTitle('New Title');
}, []);
const listItems = ⭐ useMemo(() => [5, 3, 1, 10, 9], [])
return (
<div>
// even though title doesn't change,
// items might change per ever re-evaluation
// since it is a reference type
// using only React.memo() is insufficient
// ✨ -> use useMemo()!
<DemoList
title = {listTitle}
⭐ items = {listItems}
/>
<Button onClick = {changeTitleHandler}> Change List Title </Button>
</div>
)
}
export default App;
DemoList.js
import React, { ⭐ useMemo } from 'react';
const DemoList = (props) => {
// ❗ should not use props.items as dependencies
// because it props may change
const { items } = props;
const sortedList = ⭐ useMemo(() => {
props.items.sort((a,b) => a-b);
}, [items]);
// ✨ a performance-intensive code (occupies a lot of time)
// don't want to run this code every time
// the entire component is re-evaluated
// put items as dependencies
// because if items array changes,
// it should be sorted again
return (
<div className = {classes.list}>
<h2>{props.title}</h2>
<ul>
{sorted.map((item) => (
<li key = {item}>
{item}
</li>
))}
</ul>
</div>
)
}
export default React.memo(DemoList);
usually useMemo() is used far less often then using useCallback()
because memorizing functions is much more useful,
and you need that more often than memorizing data
you essetially want to memorize data
if it would be performance-intensive
to recalculate something based on it
otherwise, it might not really be worth it
because of the costs of using useMemo