The goals
๐ช๐ปHow Does React Work Behind The Scenes?
โ๐ปUnderstanding the Virtual DOM & DOM Updates
๐๐ปUnderstanding State & State Updates
useMemo ๋ฅผ ์ฌ์ฉํ์ฌ ์ฐ์ฐํ ๊ฐ ์ฌ์ฌ์ฉํ๊ธฐ
์ด๋ฒ์๋ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ์ฌ ์ฐ์ฐ๋ ๊ฐ์ useMemo๋ผ๋ Hook ์ ์ฌ์ฉํ์ฌ ์ฌ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
App ์ปดํฌ๋ํธ์์ ๋ค์๊ณผ ๊ฐ์ด countActiveUsers ๋ผ๋ ํจ์๋ฅผ ๋ง๋ค์ด์, active ๊ฐ์ด true ์ธ ์ฌ์ฉ์์ ์๋ฅผ ์ธ์ด์ ํ๋ฉด์ ๋ ๋๋ง์ ํด๋ณด์ธ์.
App.js
import React, { useRef, useState } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
function countActiveUsers(users) {
console.log('ํ์ฑ ์ฌ์ฉ์ ์๋ฅผ ์ธ๋์ค...');
return users.filter(user => user.active).length;
}
function App() {
const [inputs, setInputs] = useState({
username: '',
email: ''
});
const { username, email } = inputs;
const onChange = e => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value
});
};
const [users, setUsers] = useState([
{
id: 1,
username: 'velopert',
email: 'public.velopert@gmail.com',
active: true
},
{
id: 2,
username: 'tester',
email: 'tester@example.com',
active: false
},
{
id: 3,
username: 'liz',
email: 'liz@example.com',
active: false
}
]);
const nextId = useRef(4);
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
const onRemove = id => {
// user.id ๊ฐ ํ๋ผ๋ฏธํฐ๋ก ์ผ์นํ์ง ์๋ ์์๋ง ์ถ์ถํด์ ์๋ก์ด ๋ฐฐ์ด์ ๋ง๋ฌ
// = user.id ๊ฐ id ์ธ ๊ฒ์ ์ ๊ฑฐํจ
setUsers(users.filter(user => user.id !== id));
};
const onToggle = id => {
setUsers(
users.map(user =>
user.id === id ? { ...user, active: !user.active } : user
)
);
};
const count = countActiveUsers(users);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle} />
<div>ํ์ฑ์ฌ์ฉ์ ์ : {count}</div>
</>
);
}
export default App;
countActiveUsers ํจ์์์ ์ฝ์์ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋๋ก ํ ์ด์ ๋, ์ด ํจ์๊ฐ ํธ์ถ๋ ๋๋ง๋ค ์ฐ๋ฆฌ๊ฐ ์์์๊ฒ ํ๊ธฐ ์ํจ์ ๋๋ค.
๊ตฌํ์ ๋ง์น๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ํ๋ ํ ๋ฐ์.
๋ค๋ฅธ ๊ณ์ ๋ช ์ ๋๋ฌ์ ์ด๋ก์์ผ๋ก ๋ง๋ค๋ฉด ํ์ฑ ์ฌ์ฉ์ ์ ๋ํ ์ ๋ฐ์ดํธ ๋ ๊ฒ์ ๋๋ค.
๊ทธ๋ฐ๋ฐ, ์ฌ๊ธฐ์ ๋ฐ์ํ๋ ์ฑ๋ฅ์ ๋ฌธ์ ๊ฐ ํ๊ฐ์ง ์์ต๋๋ค. ๋ฐ๋ก, input ์ ๊ฐ์ ๋ฐ๊ฟ๋์๋ countActiveUsers ํจ์๊ฐ ํธ์ถ๋๋ค๋ ๊ฒ ์ ๋๋ค.
ํ์ฑ ์ฌ์ฉ์ ์๋ฅผ ์ธ๋๊ฑด, users ์ ๋ณํ๊ฐ ์์๋๋ง ์ธ์ผ๋๋๊ฑด๋ฐ, input ๊ฐ์ด ๋ฐ๋ ๋์๋ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง ๋๋ฏ๋ก ์ด๋ ๊ฒ ๋ถํ์ํ ๋์๋ ํธ์ถํ์ฌ์ ์์์ด ๋ญ๋น๋๊ณ ์์ต๋๋ค.
์ด๋ฌํ ์ํฉ์๋ useMemo ๋ผ๋ Hook ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ์ฑ๋ฅ์ ์ต์ ํ ํ ์ ์์ต๋๋ค.
Memo ๋ "memoized" ๋ฅผ ์๋ฏธํ๋๋ฐ, ์ด๋, ์ด์ ์ ๊ณ์ฐ ํ ๊ฐ์ ์ฌ์ฌ์ฉํ๋ค๋ ์๋ฏธ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
ํ๋ฒ ์ฌ์ฉํด๋ณผ๊น์?
App.js
import React, { useRef, useState, useMemo } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
function countActiveUsers(users) {
console.log('ํ์ฑ ์ฌ์ฉ์ ์๋ฅผ ์ธ๋์ค...');
return users.filter(user => user.active).length;
}
function App() {
const [inputs, setInputs] = useState({
username: '',
email: ''
});
const { username, email } = inputs;
const onChange = e => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value
});
};
const [users, setUsers] = useState([
{
id: 1,
username: 'velopert',
email: 'public.velopert@gmail.com',
active: true
},
{
id: 2,
username: 'tester',
email: 'tester@example.com',
active: false
},
{
id: 3,
username: 'liz',
email: 'liz@example.com',
active: false
}
]);
const nextId = useRef(4);
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
const onRemove = id => {
// user.id ๊ฐ ํ๋ผ๋ฏธํฐ๋ก ์ผ์นํ์ง ์๋ ์์๋ง ์ถ์ถํด์ ์๋ก์ด ๋ฐฐ์ด์ ๋ง๋ฌ
// = user.id ๊ฐ id ์ธ ๊ฒ์ ์ ๊ฑฐํจ
setUsers(users.filter(user => user.id !== id));
};
const onToggle = id => {
setUsers(
users.map(user =>
user.id === id ? { ...user, active: !user.active } : user
)
);
};
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle} />
<div>ํ์ฑ์ฌ์ฉ์ ์ : {count}</div>
</>
);
}
export default App;
useMemo ์ ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์๋ ์ด๋ป๊ฒ ์ฐ์ฐํ ์ง ์ ์ํ๋ ํจ์๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ๋๊ณ ๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ์๋ deps ๋ฐฐ์ด์ ๋ฃ์ด์ฃผ๋ฉด ๋๋๋ฐ, ์ด ๋ฐฐ์ด ์์ ๋ฃ์ ๋ด์ฉ์ด ๋ฐ๋๋ฉด, ์ฐ๋ฆฌ๊ฐ ๋ฑ๋กํ ํจ์๋ฅผ ํธ์ถํด์ ๊ฐ์ ์ฐ์ฐํด์ฃผ๊ณ , ๋ง์ฝ์ ๋ด์ฉ์ด ๋ฐ๋์ง ์์๋ค๋ฉด ์ด์ ์ ์ฐ์ฐํ ๊ฐ์ ์ฌ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค.
ํ๋ฒ ๊ณ์ ๋ช ๋ค์ ํด๋ฆญ๋ ํด๋ณด๊ณ , input ์ ์์ ๋ ํด๋ณด์ธ์.
๊ทธ๋ผ ์ต์ ํ์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ์ ์ ์ฝํ๊ธฐ ์ํ ๋ฐฉ์์ผ๋ก useMemo๋ฅผ ์๋ฌด๊ณณ์๋ ์ฌ์ฉํ ์ ์์ง ์์๊น?
๋ต์ nej
์ต์ ํ๋ฅผ ํด์ฃผ๋ ๋์ ์ ์ด๋์ ๋์ ๋น์ฉ์ด ์๊ตฌ๋จ.
๊ทธ ๋น์ฉ์ด๋ผํ๋ฉด ์ด์ props๊ฐ์ ๊ธฐ์ตํ๊ณ ์์ด์ผ ํ๋ค๋ ๊ฒ๊ณผ ๋น๊ต๋ฅผ ํ๋ ๋์์ ํด์ผ ํ๋ค๋ ๊ฒ!
useCallback ์ ์ฌ์ฉํ์ฌ ํจ์ ์ฌ์ฌ์ฉํ๊ธฐ
useCallback ์ ์ฐ๋ฆฌ๊ฐ ์ง๋ ์๊ฐ์ ๋ฐฐ์ ๋ useMemo ์ ๋น์ทํ Hook ์ ๋๋ค.
useMemo ๋ ํน์ ๊ฒฐ๊ณผ๊ฐ์ ์ฌ์ฌ์ฉ ํ ๋ ์ฌ์ฉํ๋ ๋ฐ๋ฉด, useCallback ์ ํน์ ํจ์๋ฅผ ์๋ก ๋ง๋ค์ง ์๊ณ ์ฌ์ฌ์ฉํ๊ณ ์ถ์๋ ์ฌ์ฉํฉ๋๋ค.
์ด์ ์ App.js ์์ ๊ตฌํํ์๋ onCreate, onRemove, onToggle ํจ์๋ฅผ ํ์ธํด๋ด ์๋ค.
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
const onRemove = id => {
// user.id ๊ฐ ํ๋ผ๋ฏธํฐ๋ก ์ผ์นํ์ง ์๋ ์์๋ง ์ถ์ถํด์ ์๋ก์ด ๋ฐฐ์ด์ ๋ง๋ฌ
// = user.id ๊ฐ id ์ธ ๊ฒ์ ์ ๊ฑฐํจ
setUsers(users.filter(user => user.id !== id));
};
const onToggle = id => {
setUsers(
users.map(user =>
user.id === id ? { ...user, active: !user.active } : user
)
);
};
์ด ํจ์๋ค์ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง ๋ ๋ ๋ง๋ค ์๋ก ๋ง๋ค์ด์ง๋๋ค. ํจ์๋ฅผ ์ ์ธํ๋ ๊ฒ ์์ฒด๋ ์ฌ์ค ๋ฉ๋ชจ๋ฆฌ๋, CPU ๋ ๋ฆฌ์์ค๋ฅผ ๋ง์ด ์ฐจ์ง ํ๋ ์์ ์ ์๋๊ธฐ ๋๋ฌธ์ ํจ์๋ฅผ ์๋ก ์ ์ธํ๋ค๊ณ ํด์ ๊ทธ ์์ฒด ๋ง์ผ๋ก ํฐ ๋ถํ๊ฐ ์๊ธธ์ผ์ ์์ง๋ง, ํ๋ฒ ๋ง๋ ํจ์๋ฅผ ํ์ํ ๋๋ง ์๋ก ๋ง๋ค๊ณ ์ฌ์ฌ์ฉํ๋ ๊ฒ์ ์ฌ์ ํ ์ค์ํฉ๋๋ค.
๊ทธ ์ด์ ๋, ์ฐ๋ฆฌ๊ฐ ๋์ค์ ์ปดํฌ๋ํธ์์ props ๊ฐ ๋ฐ๋์ง ์์์ผ๋ฉด Virtual DOM ์ ์๋ก ๋ ๋๋งํ๋ ๊ฒ ์กฐ์ฐจ ํ์ง ์๊ณ ์ปดํฌ๋ํธ์ ๊ฒฐ๊ณผ๋ฌผ์ ์ฌ์ฌ์ฉ ํ๋ ์ต์ ํ ์์ ์ ํ ๊ฑด๋ฐ์, ์ด ์์ ์ ํ๋ ค๋ฉด, ํจ์๋ฅผ ์ฌ์ฌ์ฉํ๋๊ฒ์ด ํ์์ ๋๋ค.
useCallback ์ ์ด๋ฐ์์ผ๋ก ์ฌ์ฉํฉ๋๋ค.
App.js
import React, { useRef, useState, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
function countActiveUsers(users) {
console.log('ํ์ฑ ์ฌ์ฉ์ ์๋ฅผ ์ธ๋์ค...');
return users.filter(user => user.active).length;
}
function App() {
const [inputs, setInputs] = useState({
username: '',
email: ''
});
const { username, email } = inputs;
const onChange = useCallback(
e => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value
});
},
[inputs]
);
const [users, setUsers] = useState([
{
id: 1,
username: 'velopert',
email: 'public.velopert@gmail.com',
active: true
},
{
id: 2,
username: 'tester',
email: 'tester@example.com',
active: false
},
{
id: 3,
username: 'liz',
email: 'liz@example.com',
active: false
}
]);
const nextId = useRef(4);
const onCreate = useCallback(() => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
}, [users, username, email]);
const onRemove = useCallback(
id => {
// user.id ๊ฐ ํ๋ผ๋ฏธํฐ๋ก ์ผ์นํ์ง ์๋ ์์๋ง ์ถ์ถํด์ ์๋ก์ด ๋ฐฐ์ด์ ๋ง๋ฌ
// = user.id ๊ฐ id ์ธ ๊ฒ์ ์ ๊ฑฐํจ
setUsers(users.filter(user => user.id !== id));
},
[users]
);
const onToggle = useCallback(
id => {
setUsers(
users.map(user =>
user.id === id ? { ...user, active: !user.active } : user
)
);
},
[users]
);
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle} />
<div>ํ์ฑ์ฌ์ฉ์ ์ : {count}</div>
</>
);
}
export default App;
์ฃผ์ ํ์ค ์ ์, ํจ์ ์์์ ์ฌ์ฉํ๋ ์ํ ํน์ props ๊ฐ ์๋ค๋ฉด ๊ผญ, deps ๋ฐฐ์ด์์ ํฌํจ์์ผ์ผ ๋๋ค๋ ๊ฒ ์ ๋๋ค. ๋ง์ฝ์ deps ๋ฐฐ์ด ์์ ํจ์์์ ์ฌ์ฉํ๋ ๊ฐ์ ๋ฃ์ง ์๊ฒ ๋๋ค๋ฉด, ํจ์ ๋ด์์ ํด๋น ๊ฐ๋ค์ ์ฐธ์กฐํ ๋ ๊ฐ์ฅ ์ต์ ๊ฐ์ ์ฐธ์กฐ ํ ๊ฒ์ด๋ผ๊ณ ๋ณด์ฅ ํ ์ ์์ต๋๋ค. props ๋ก ๋ฐ์์จ ํจ์๊ฐ ์๋ค๋ฉด, ์ด ๋ํ deps ์ ๋ฃ์ด์ฃผ์ด์ผ ํด์.
์ฌ์ค, useCallback ์ useMemo ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ก์ต๋๋ค. ๋ค๋ง, ํจ์๋ฅผ ์ํด์ ์ฌ์ฉ ํ ๋ ๋์ฑ ํธํ๊ฒ ํด์ค ๊ฒ ๋ฟ์ด์ง์. ์ด๋ฐ์์ผ๋ก๋ ํํ ํ ์ ์์ต๋๋ค.
const onToggle = useMemo(
() => () => {
/* ... */
},
[users]
);
useCallback ์ ์ฌ์ฉ ํจ์ผ๋ก์จ, ๋ฐ๋ก ์ด๋ค๋ผ์ ์๋ ๋์ ๋๋ ์ต์ ํ๋ ์์ต๋๋ค. ๋ค์ ์์์์, ์ปดํฌ๋ํธ ๋ ๋๋ง ์ต์ ํ ์์ ์ ํด์ฃผ์ด์ผ๋ง ์ฑ๋ฅ์ด ์ต์ ํ๋๋๋ฐ์, ๊ทธ ์ ์, ์ด๋ค ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋๊ณ ์๋์ง ํ์ธํ๊ธฐ ์ํด์ React DevTools ๋ผ๋ ๊ฒ์ ์๊ฐ๋๋ฆฌ๊ฒ ์ต๋๋ค.
์ฐ์ , ๊ตฌ๊ธ์ React DevTools ๋ฅผ ๊ฒ์ํด์ ํฌ๋กฌ ์น์คํ ์ด์ ๋ค์ด๊ฐ๋ค, ํฌ๋กฌ ํ์ฅ ํ๋ก๊ทธ๋จ์ ์ค์นํด์ฃผ์ธ์. ๋งํฌ
์ค์น๋ฅผ ํ๊ณ ๋๋ฉด ๋ค์๊ณผ ๊ฐ์ด React ํญ์ด ๊ฐ๋ฐ์ ๋๊ตฌ์ ๋น๋๋ค. ํฑ๋๋ฐํด ์์ด์ฝ์ ๋๋ฅด๊ณ , 'Highlight Updates' ๋ฅผ ์ฒดํฌํด์ฃผ์ธ์.
์ด ์์ฑ์ ํค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ฆฌ๋ ๋๋ง ๋๋ ์ปดํฌ๋ํธ์ ์ฌ๊ฐํ ํํ๋ก ํ์ด๋ผ์ดํธ๋์ด ๋ณด์ฌ์ง๊ฒ ๋ฉ๋๋ค.
์ง๊ธ ๋ณด๋ฉด, input ์ด ๋ฐ๋ ๋์๋ UserList ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง์ด ๋๊ณ ์์ง์?
๋ค์ ์์์์๋ ์ด ๋ฆฌ๋ ๋๋ง์ ๋ง์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
Summary 1.
React -> usually we work with Functional Component which returns JSX syntax
Props-> we can give props to each component
Whenever we change the state of component, the componenet will be re-evaluated -> the component function executes again
React just brings the latest screen shot and compare it to previous one and if there is some differeces then React deliver it to React DOM because we are using React DOM to render the files.
When we re-evaluate, we run the codes again at the same time functions also + output JSX syntax also.
So to prevent unneccessory re-execution of code, we can use 'React.memo'
but some case that we set the relative value with using '=' then it can't be controlled by 'React.memo', then we can use 'useCallback'
Then every time if the App functions runs again whenever the state changes, doesn't this mean that we will re-initialize our code? and re-execute 'useState' over and over again?
Sources
https://react.vlpt.us/basic/17-useMemo.html
17. useMemo ๋ฅผ ์ฌ์ฉํ์ฌ ์ฐ์ฐํ ๊ฐ ์ฌ์ฌ์ฉํ๊ธฐ · GitBook
17. useMemo ๋ฅผ ์ฌ์ฉํ์ฌ ์ฐ์ฐํ ๊ฐ ์ฌ์ฌ์ฉํ๊ธฐ ์ด๋ฒ์๋ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ์ฌ ์ฐ์ฐ๋ ๊ฐ์ useMemo๋ผ๋ Hook ์ ์ฌ์ฉํ์ฌ ์ฌ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. App ์ปดํฌ๋ํธ์์ ๋ค์๊ณผ ๊ฐ์ด co
react.vlpt.us
https://academind.com/tutorials/reference-vs-primitive-values
Reference vs Primitive Values
Learn why most people copy objects and arrays in JavaScript incorrectly. And why you won't make that mistake!
academind.com
https://velog.io/@ggong/useState-Hook%EA%B3%BC-%ED%81%B4%EB%A1%9C%EC%A0%80
useState Hook๊ณผ ํด๋ก์
์ ์ ๊ธฐ์ ๋ฉด์ ์ ๋ณด๋ฉด์ "React hook์์ ํด๋ก์ ๊ฐ ์ด๋ป๊ฒ ์ฐ์ด๋์ง ์ค๋ช ํด๋ณด์ธ์" ๋ผ๋ ์ง๋ฌธ์ ๋ฐ์ ์ ์ด ์์๋ค. ๋๋ฆ ๋ฆฌ์กํธ๋ฅผ ์ค๋ ์ผ๋ค๊ณ ์๊ฐํ๋๋ฐ, ํด๋ก์ ์ ๋ํด์๋ ๋ฐฉ๊ธ ์ค๋ช ํ๋๋ฐ..
velog.io
https://yeoulcoding.tistory.com/149#recentEntries
[React] ํด๋ก์ ์ useState Hooks
Overview React Hooks๋ React Functional Component(ํจ์ํ ์ปดํฌ๋ํธ)์์ ์ํ๊ด๋ฆฌ ๋ฐ ์ปดํฌ๋ํธ ์๋ช ์ฃผ๊ธฐ API(Lifecycle API) ๋ฑ ํด๋์ค ์ปดํฌ๋ํธ์์๋ง ์ง์ํ๋ ๊ธฐ๋ฅ๋ค์ ์ฌ์ฉํ ์ ์๋๋ก ๋์์ค๋๋ค. ํจ์ํ
yeoulcoding.me