๐กReact.memo?
- ์ปดํฌ๋ํธ์ props๊ฐ ๋ฐ๋์ง ์์๋ค๋ฉด, ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ์ฌ ์ปดํฌ๋ํธ์ ๋ฆฌ๋ ๋๋ง ์ฑ๋ฅ์ ์ต์ ํ ์์ผ์ฃผ๋ ํจ์!
- ์ด ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด, ์ปดํฌ๋ํธ์์ ๋ฆฌ๋ ๋๋ง์ด ํ์ํ ์ํฉ์์๋ง ๋ฆฌ๋ ๋๋ง์ ํ๋๋ก ์ค์ ํด์ค ์ ์๋ค.
- ์ฌ์ฉ๋ฒ์ ๊ทธ๋ฅ ๊ฐ์ธ์ฃผ๋ฉด ๋จ!
CreateUser.js ์ ์ ์ฉ
// ์ปดํฌ๋ํธ ๋ฆฌ๋ ๋๋ง ๋ฐฉ์ง๋ฅผ ์ํ React.memo
export default React.memo(CreateUser);
์ ์ฌ์!
UserList.js ์ ์ ์ฉ
import React, {useEffect} from 'react'
// user๋ฆฌ์คํธ๋ค์ด ๋ณด์ฌ์ง๋ ๋ถ๋ถ
const User = React.memo(function User({user, onRemove, onToggle}) {
return(
<div>
<span
style={{
cursor: 'pointer',
color: user.active ? 'green':'black',
}}
onClick={()=>onToggle(user.id)}
>{user.username}</span>
<span>{user.email}</span>
<button onClick={()=>onRemove(user.id)}>์ญ์ </button>
</div>
)
});
function UserList({users, onRemove, onToggle}) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove} onToggle={onToggle}/>
))}
</div>
)
}
export default React.memo(UserList);
-> ์ด๋ ๊ฒ ์ ์ฉ์ ๋คํ๋ฉด, input ์์ ์ ํ ๋ ํ๋จ์ UserList๊ฐ ๋ฆฌ๋ ๋๋ง ๋์ง ์๋๊ฒ์ ํ์ธํ ์ ์๋ค.
๐ซํ์ง๋ง UserList์ User์ค ํ๋๋ผ๋ ์์ ํ๋ฉด ๋ชจ๋ User ๋ค์ด ๋ฆฌ๋ ๋๋ง ๋๊ณ , CreateUser(input๊ณผ ๋ฑ๋ก ๋ฒํผ ๋ถ๋ถ)๋ ๋ฆฌ๋ ๋๋ง์ด ๋๋ค. -> ์ด์ ๋ users ๋ฐฐ์ด์ด ๋ฐ๋ ๋ ๋ง๋ค onCreate, onToggle, onRemove ๋ชจ๋ ์๋ก ๋ง๋ค์ด์ง๊ธฐ ๋๋ฌธ์ด๋ค.
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 => {
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])
-> ์ด๋ ๊ฒ 3๊ฐ์ ์ด๋ฒคํธ ์์ deps์ users๊ฐ ๋ค์ด๊ฐ ์๊ธฐ ๋๋ฌธ์, ๋ฐฐ์ด์ด ๋ฐ๋ ๋๋ง๋ค ํจ์๊ฐ ์๋ก ๋ง๋ค์ด์ง๋ ๊ฒ์ ๋น์ฐํ๋ค.
๐ก๊ทธ๋ผ ์ด๊ฑธ ์ต์ ํ ํ๊ณ ์ถ๋ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
- deps์์ users๋ฅผ ์ง์ด๋ค.
- ํจ์๋ค์์ ํ์ฌ useState๋ก ๊ด๋ฆฌํ๋ users๋ฅผ ์ฐธ์กฐํ์ง ์๊ฒ ํ๋ค.
- 2๋ฒ์ ์ด๋ป๊ฒ ํ ๊น? ๋ฐ๋ก ํจ์ํ ์ ๋ฐ์ดํธ๋ก!
โฐํจ์ํ ์ ๋ฐ์ดํธ
- ํจ์ํ ์ ๋ฐ์ดํธ๋ useState์ ๊ฐ์ ๊ทธ๋๋ก ์ ๋ฌํ๋ ๊ฒ์ด ์๋๋ผ, ํจ์๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ด๋ค
- ํจ์ํ ์ ๋ฐ์ดํธ๋ฅผ ํ๊ฒ ๋๋ฉด, setUsers์ ๋ฑ๋กํ๋ ์ฝ๋ฐฑํจ์์ ํ๋ผ๋ฏธํฐ์์ ์ต์ users๋ฅผ ์ฐธ์กฐํ ์ ์๊ธฐ ๋๋ฌธ์ deps ์ users๋ฅผ ๋ฃ์ง ์์๋ ๋๋ค.
๊ธฐ์กด์ deps์ ๋ฐฐ์ด์ด ์์ผ๋ฉด ๊ทธ ํด๋น ๋ฐฐ์ด์ด ๋ฐ๋ ๋๋ง๋ค onToggle๋ฑ์ ํจ์๊ฐ ์์ ์๋ก ๋ง๋ค์ด์ง๋๋ฐ, ํจ์ํ ์ ๋ฐ์ดํธ๋ฅผ ํตํด deps๋ฅผ ๋ฐ์ง ์๊ณ setUsers์ ์ต์ users๋ฅผ ์ฐธ์กฐํ๋ ํจ์์ ํํ๋ก ๋ง๋ ๋ค๋ฉด ํจ์๊ฐ ๋ค์ ๋ง๋ค์ด์ง์ง ์๋๋ค..?!
๊ทธ๋ผ ๊ฐ ํจ์๋ค์ ์ ๋ฐ์ดํธ ํด๋ณด์! (onChange์ ๊ฒฝ์ฐ์ ํจ์ํ ์ ๋ฐ์ดํธ๋ฅผ ํด๋ ์ํฅ์ ๊ฐ์ง ์์ง๋ง, ์ฐ์ต์ผ์ ํด๋ณด์!)
import React,{useRef, useState, useMemo, useCallback} from 'react'; // useCallback ๋ถ๋ฌ์ค๊ธฐ
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๋ฅผ ์ฐธ์กฐ ํ ์ ์๋ค.
setInputs(inputs => ({
...inputs,
[name]: value
}))
// deps ๋น์ฐ๊ธฐ
}, []
);
const [users, setUsers] = useState(
[
{id: 1, username: 'John', email: 'john@example.com',
active: true,
},
{id: 2, username: 'Car', email: 'car@example.com',
active: false,
},
{id: 3, username: 'lee', email: 'lee@example.com',
active: false,
},
]
)
const nextId = useRef(4);
const onCreate = useCallback(() => {
const user = {
id: nextId.current,
username,
email
};
// setUsers์ ๋ฑ๋กํ๋ ์ฝ๋ฐฑํจ์ ํ๋ผ๋ฏธํฐ์์ ์ต์ users๋ฅผ ์ฐธ์กฐํ ์ ์๋ค.
setUsers(users => users.concat(user));
setInputs({
username: '',
email: ''
})
nextId.current += 1;
// deps์์ users ์ง์ฐ๊ธฐ
},[username, email]);
const onRemove = useCallback(id => {
// setUsers์ ๋ฑ๋กํ๋ ์ฝ๋ฐฑํจ์ ํ๋ผ๋ฏธํฐ์์ ์ต์ users๋ฅผ ์ฐธ์กฐํ ์ ์๋ค.
setUsers(users => users.filter(user => user.id !== id))
// deps์์ users ์ง์ฐ๊ธฐ
},[]);
const onToggle = useCallback(id => {
// setUsers์ ๋ฑ๋กํ๋ ์ฝ๋ฐฑํจ์ ํ๋ผ๋ฏธํฐ์์ ์ต์ users๋ฅผ ์ฐธ์กฐํ ์ ์๋ค.
setUsers(users =>
users.map(user =>
user.id === id ? { ...user, active: !user.active} : user
)
)
// deps์์ 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;
-> ์ด๋ ๊ฒ ํด์ฃผ๋ฉด ํน์ ํญ๋ชฉ์ ์์ ํ ๋, ํด๋น ํญ๋ชฉ๋ง ๋ ๋๋ง ๋๋ฉฐ ์ต์ ํ๊ฐ ๋๋ ๊ฒ์ด๋ค!
์ค์ ๋ฆฌ์กํธ ๊ฐ๋ฐ์,
๊ณ์ ๋ฐฐ์ ๋ useCallback(ํจ์ ์ฌ์ฌ์ฉ), useMemo(๊ฐ ์ฌ์ฌ์ฉ), React.memo(๋ฆฌ๋ ๋๋ง ๋ฐฉ์ง)๋ ์ปดํฌ๋ํธ์ ์ฑ๋ฅ์ ์ค์ ๋ก ๊ฐ์ ํ ์ ์๋ ์ํฉ์์๋ง ํ๋ค.
ex) User ์ปดํฌ๋ํธ์ b์ button์ onClick์ผ๋ก ์ค์ ํด์ค ํจ์๋ค์ ํด๋น ํจ์๋ค์ useCallback์ผ๋ก ์ฌ์ฌ์ฉํ๋คํด์ ๋ฆฌ๋ ๋๋ง์ ๋ง์ ์ ์๋ ๊ฒ์ ์๋๋ฏ๋ก ๊ตณ์ด ๊ทธ๋ ๊ฒ ํ ํ์๋ ์๋ค.
โ ๋ ๋๋ง์ ์ต์ ํ ํ์ง ์์ ์ปดํฌ๋ํธ์ React.memo๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ถํ์ํ props ๋น๊ต๋ง ํ๋ ๊ฒ์ด๋ค.
โReact.memo์์ ๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ์ propsAreEqual ์ด๋ผ๋ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ๊ฐ๋ค๋ง ๋น๊ต๋ฅผ ํ๋ ๊ฒ๋ ๊ฐ๋ฅํ๋ค.
export default React.memo(
UserList,
(prevProps, nextProps) => prevProps.users === nextProps.users
);
-> ํ์ง๋ง ์ฌ๊ธฐ์ ์๋์น ์์ ๋ฒ๊ทธ๋ค์ด ๋ฐ์ํ ์ ์๋ค.
์๋ฅผ๋ค์ด, ํจ์ํ ์ ๋ฐ์ดํธ๋ก ์ ํ์ ์ํ๋๋ฐ ์ด๋ ๊ฒ users๋ง ๋น๊ต๋ฅผ ํ๊ฒ ๋๋ค๋ฉด onToggle๊ณผ onRemove์์ ์ต์ users๋ฐฐ์ด์ ์ฐธ์กฐํ์ง ์์ผ๋ฏ๋ก ์ฌ๊ฐํ ์ค๋ฅ ๋ฐ์!
Reference
https://velog.io/@suyeonme/react-useState์-๋น๋๊ธฐ์ -์์ฑ-ํจ์ํ-์ ๋ฐ์ดํธ
'React > Velopert's React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[React] Study #20 | ์ปค์คํ Hooks ๋ง๋ค๊ธฐ (0) | 2021.10.09 |
---|---|
[React] Study #19 | useReducer : ์ํ ์ ๋ฐ์ดํธ ๋ก์ง ๋ถ๋ฆฌ (0) | 2021.10.09 |
[React] Study #17 | useCallback (0) | 2021.10.08 |
[React] Study # 16 | useMemo (0) | 2021.10.08 |
[React] Study #15 | useEffect (0) | 2021.10.08 |