๐น ๋ฒจ๋กํผํธ์ ํจ๊ปํ๋ ๋ชจ๋ ๋ฆฌ์กํธ
~ for ์ฑ๋ฅ ์ต์ ํ ~
useMemo : ์ฐ์ฐํ ๊ฐ ์ฌ์ฌ์ฉ
useCallback : ํจ์ ์ฌ์ฌ์ฉ
React.memo : ์ปดํฌ๋ํธ ๋ฆฌ๋ ๋๋ง ๋ฐฉ์ง
7. useState๋ฅผ ํตํด ์ปดํฌ๋ํธ์์ ๋ฐ๋๋ ๊ฐ ๊ด๋ฆฌํ๊ธฐ
import React, { useState } from 'react';
const [number, setNumber] = useState(0);
์ํ์ ๊ธฐ๋ณธ๊ฐ์ ํ๋ผ๋ฏธํฐ๋ก ๋ฃ์ด์ ํธ์ถ
ํธ์ถํ๋ฉด ๋ฐฐ์ด์ด ๋ฐํ, ์ฒซ๋ฒ์งธ ์์๋ ํ์ฌ ์ํ, ๋๋ฒ์งธ ์์๋ Setter ํจ์
11. useRef๋ก ํน์ DOM ์ ํํ๊ธฐ
import React, { useRef } from 'react';
const nameInput = useRef();
...
nameInput.current.focus();
...
<input
name="name"
placeholder="์ด๋ฆ"
onChange={onChange}
value={name}
ref={nameInput}
/>
12. useRef๋ก ์ปดํฌ๋ํธ ์์ ๋ณ์ ๋ง๋ค๊ธฐ
const nextId = useRef(4);
...
nextId.current += 1;
.currnet์ ๊ธฐ๋ณธ๊ฐ์ ํ๋ผ๋ฏธํฐ๋ก ๋ฃ์ด์ ํธ์ถ
์ด ๊ฐ์ ์์ ํ ๋์๋ .currnet ๊ฐ์ ์์ ํ๋ฉด ๋๊ณ , ์กฐํํ ๋์๋ .current๋ฅผ ์กฐํํ๋ฉด ๋๋ค.
13. useEffect๋ฅผ ์ฌ์ฉํ์ฌ ๋ง์ดํธ(์ฒ์ ๋ํ๋ฌ์ ๋)/์ธ๋ง์ดํธ(์ฌ๋ผ์ง ๋)/์ ๋ฐ์ดํธ(ํน์ props๊ฐ ๋ฐ๋ ๋)์ ํ ์์ ์ค์ ํ๊ธฐ
import React, { useEffect } from 'react';
[๋ง์ดํธ / ์ธ๋ง์ดํธ]
<UserList.js>
import React, { useEffect } from 'react';
function User({ user, onRemove, onToggle }) {
useEffect(() => {
console.log('์ปดํฌ๋ํธ๊ฐ ํ๋ฉด์ ๋ํ๋จ');
return () => {
console.log('์ปดํฌ๋ํธ๊ฐ ํ๋ฉด์์ ์ฌ๋ผ์ง');
};
}, []);
return (
<div>
<b
style={{
cursor: 'pointer',
color: user.active ? 'green' : 'black'
}}
onClick={() => onToggle(user.id)}
>
{user.username}
</b>
<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 UserList;
useEffect ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์๋ ํจ์, ๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ์๋ ์์กด๊ฐ์ด ๋ค์ด์๋ ๋ฐฐ์ด(deps)๋ฅผ ๋ฃ๋๋ค.
๋ง์ฝ deps ๋ฐฐ์ด์ ๋น์๊ฒ ๋๋ค๋ฉด, ์ปดํฌ๋ํธ๊ฐ ์ฒ์ ๋ํ๋ ๋์๋ง useEffect์ ๋ฑ๋กํ ํจ์๊ฐ ํธ์ถ๋๋ค.
useEffect์์๋ ํจ์๋ฅผ ๋ฐํํ ์ ์๋๋ฐ, ์ด๋ฅผ cleanup ํจ์๋ผ๊ณ ํ๋ค.
cleanup ํจ์๋ useEffect์ ๋ํ ๋ท์ ๋ฆฌ๋ฅผ ํด์ค๋ค๊ณ ๋ณด๋ฉด๋๋๋ฐ,
deps๊ฐ ๋น์ด์๋ ๊ฒฝ์ฐ์๋ ์ปดํฌ๋ํธ๊ฐ ์ฌ๋ผ์ง ๋ cleanup ํจ์๊ฐ ํธ์ถ๋๋ค.
[deps์ ํน์ ๊ฐ ๋ฃ๊ธฐ]
deps์ ํน์ ๊ฐ์ ๋ฃ๊ฒ ๋๋ฉด, ์ปดํฌ๋ํธ๊ฐ ์ฒ์ ๋ง์ดํธ ๋ ๋, ์ง์ ํ ๊ฐ์ด ๋ฐ๋ ๋, ์ธ๋ง์ดํธ ๋ ๋, ๊ฐ์ด ๋ฐ๋๊ธฐ ์ง์ ์๋ ํธ์ถ์ด ๋๋ค.
<UserList.js>
import React, { useEffect } from 'react';
function User({ user, onRemove, onToggle }) {
useEffect(() => {
console.log('user ๊ฐ์ด ์ค์ ๋จ');
console.log(user);
return () => {
console.log('user ๊ฐ ๋ฐ๋๊ธฐ ์ ..');
console.log(user);
};
}, [user]);
return (
<div>
<b
style={{
cursor: 'pointer',
color: user.active ? 'green' : 'black'
}}
onClick={() => onToggle(user.id)}
>
{user.username}
</b>
<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 UserList;
useEffect์์ ์ฌ์ฉํ๋ ์ํ๋ props๊ฐ ์๋ค๋ฉด, useEffect์ deps์ ๋ฃ์ด์ฃผ์ด์ผ ํ๋ค.
๊ทธ๋ ์ง ์์ผ๋ฉด useEffect์ ๋ฑ๋กํ ํจ์๊ฐ ์ต์ ์ํ๋ porps๋ฅผ ๊ฐ๋ฅดํค์ง ์๊ฒ ๋๋ค.
[deps ํ๋ผ๋ฏธํฐ๋ฅผ ์๋ตํ๊ธฐ]
deps ํ๋ผ๋ฏธํฐ๋ฅผ ์๋ตํ๋ฉด, ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง ๋ ๋๋ง๋ค ํธ์ถ๋๋ค.
<UserList.js>
import React, { useEffect } from 'react';
function User({ user, onRemove, onToggle }) {
useEffect(() => {
console.log(user);
});
return (
<div>
<b
style={{
cursor: 'pointer',
color: user.active ? 'green' : 'black'
}}
onClick={() => onToggle(user.id)}
>
{user.username}
</b>
<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 UserList;
17. useMemo๋ฅผ ์ฌ์ฉํ์ฌ ์ฐ์ฐํ ๊ฐ ์ฌ์ฌ์ฉํ๊ธฐ for ์ฑ๋ฅ ์ต์ ํ
[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 ๋ฐฐ์ด์ ๋ฃ์ด์ค๋ค.
์ด ๋ฐฐ์ด ์์ ๋ฃ์ ๋ด์ฉ์ด ๋ฐ๋๋ฉด, ๋ฑ๋กํ ํจ์๋ฅผ ํธ์ถํด์ ๊ฐ์ ์ฐ์ฐํด์ฃผ๊ณ , ๋ฐ๋์ง ์์ผ๋ฉด ์ด์ ์ ์ฐ์ฐํ ๊ฐ์ ์ฌ์ฌ์ฉํ๋ค.
18. useCallback์ ์ฌ์ฉํ์ฌ ํจ์ ์ฌ์ฌ์ฉํ๊ธฐ for ์ฑ๋ฅ ์ต์ ํ
<App.js>
import React, { useCallback } from 'react';
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;
useCallback ํจ์ ์์์๋ ์ฌ์ฉํ๋ ์ํ ํน์ props๊ฐ ์๋ค๋ฉด ๊ผญ deps ๋ฐฐ์ด์์ ํฌํจ์์ผ์ผ ํ๋ค.
๋ง์ฝ์ ๋ฃ์ง ์์ผ๋ฉด, ํจ์ ๋ด์์ ํด๋น ๊ฐ๋ค์ ์ฐธ์กฐํ ๋ ๊ฐ์ฅ ์ต์ ๊ฐ์ ์ฐธ์กฐํ ๊ฒ์ด๋ผ๊ณ ๋ณด์ฅํ ์ ์๋ค.
19. React.memo๋ฅผ ์ฌ์ฉํ ์ปดํฌ๋ํธ ๋ฆฌ๋ ๋๋ง ๋ฐฉ์ง for ์ฑ๋ฅ ์ต์ ํ
<CreateUser.js>
import React from 'react';
const CreateUser = ({ username, email, onChange, onCreate }) => {
return (
<div>
<input
name="username"
placeholder="๊ณ์ ๋ช
"
onChange={onChange}
value={username}
/>
<input
name="email"
placeholder="์ด๋ฉ์ผ"
onChange={onChange}
value={email}
/>
<button onClick={onCreate}>๋ฑ๋ก</button>
</div>
);
};
export default React.memo(CreateUser);
<UesrList.js>
import React from 'react';
const User = React.memo(function User({ user, onRemove, onToggle }) {
return (
<div>
<b
style={{
cursor: 'pointer',
color: user.active ? 'green' : 'black'
}}
onClick={() => onToggle(user.id)}
>
{user.username}
</b>
<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);
โญ ๋ฐฐ์ด์ด ๋ฐ๋ ๋๋ง๋ค ํจ์๊ฐ ์๋ก ๋ง๋ค์ด์ง๋ค...
์ด๊ฑธ ์ต์ ํํ๊ณ ์ถ๋ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
deps์ users(๋ฐฐ์ด)์ ์ง์ฐ๊ณ , ํจ์๋ค์์ ํ์ฌ useState๋ก ๊ด๋ฆฌํ๋ users๋ฅผ ์ฐธ์กฐํ์ง ์๊ฒ ํ๋ค.
=> ํจ์ํ ์ ๋ฐ์ดํธ !!!!!!!
[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 => ({
...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 = useCallback(() => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users => users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
}, [username, email]);
const onRemove = useCallback(id => {
// user.id ๊ฐ ํ๋ผ๋ฏธํฐ๋ก ์ผ์นํ์ง ์๋ ์์๋ง ์ถ์ถํด์ ์๋ก์ด ๋ฐฐ์ด์ ๋ง๋ฌ
// = user.id ๊ฐ id ์ธ ๊ฒ์ ์ ๊ฑฐํจ
setUsers(users => users.filter(user => user.id !== id));
}, []);
const onToggle = useCallback(id => {
setUsers(users =>
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;
20. useReducer๋ฅผ ์ฌ์ฉํ์ฌ ์ํ ์ ๋ฐ์ดํธ ๋ก์ง ๋ถ๋ฆฌํ๊ธฐ
reducer : ํ์ฌ ์ํ์ ์ก์ ๊ฐ์ฒด๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์์์ ์๋ก์ด ์ํ๋ฅผ ๋ฐํํด์ฃผ๋ ํจ์
action : ์ ๋ฐ์ดํธ๋ฅผ ์ํ ์ ๋ณด
import React, { useReducer } from 'react';
<reducer ๋>
function reducer(state, action) {
// ์๋ก์ด ์ํ๋ฅผ ๋ง๋๋ ๋ก์ง
// const nextState = ...
return nextState;
}
<action ์์>
// ์นด์ดํฐ์ 1์ ๋ํ๋ ์ก์
{
type: 'INCREMENT'
}
// ์นด์ดํฐ์ 1์ ๋นผ๋ ์ก์
{
type: 'DECREMENT'
}
// input ๊ฐ์ ๋ฐ๊พธ๋ ์ก์
{
type: 'CHANGE_INPUT',
key: 'email',
value: 'tester@react.com'
}
// ์ ํ ์ผ์ ๋ฑ๋กํ๋ ์ก์
{
type: 'ADD_TODO',
todo: {
id: 1,
text: 'useReducer ๋ฐฐ์ฐ๊ธฐ',
done: false,
}
}
<useReducer ์ฌ์ฉ๋ฒ>
const [state, dispatch] = useReducer(reducer, initialState);
state : ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ ์ ์๋ ์ํ
dispatch : ์ก์ ์ ๋ฐ์์ํค๋ ํจ์ dispatch({type: 'INCREMENT'});
useReducer์ ๋ฃ๋ ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ๋ reducerํจ์, ๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ๋ ์ด๊ธฐ์ํ
<Counter.js>
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
function Counter() {
const [number, dispatch] = useReducer(reducer, 0);
const onIncrease = () => {
dispatch({ type: 'INCREMENT' });
};
const onDecrease = () => {
dispatch({ type: 'DECREMENT' });
};
return (
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
<App.js>
import React, { useRef, useReducer, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
function countActiveUsers(users) {
console.log('ํ์ฑ ์ฌ์ฉ์ ์๋ฅผ ์ธ๋์ค...');
return users.filter(user => user.active).length;
}
const initialState = {
inputs: {
username: '',
email: ''
},
users: [
{
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
}
]
};
function reducer(state, action) {
switch (action.type) {
case 'CHANGE_INPUT':
return {
...state,
inputs: {
...state.inputs,
[action.name]: action.value
}
};
case 'CREATE_USER':
return {
inputs: initialState.inputs,
users: state.users.concat(action.user)
};
case 'TOGGLE_USER':
return {
...state,
users: state.users.map(user =>
user.id === action.id ? { ...user, active: !user.active } : user
)
};
case 'REMOVE_USER':
return {
...state,
users: state.users.filter(user => user.id !== action.id)
};
default:
return state;
}
}
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
const nextId = useRef(4);
const { users } = state;
const { username, email } = state.inputs;
const onChange = useCallback(e => {
const { name, value } = e.target;
dispatch({
type: 'CHANGE_INPUT',
name,
value
});
}, []);
const onCreate = useCallback(() => {
dispatch({
type: 'CREATE_USER',
user: {
id: nextId.current,
username,
email
}
});
nextId.current += 1;
}, [username, email]);
const onToggle = useCallback(id => {
dispatch({
type: 'TOGGLE_USER',
id
});
}, []);
const onRemove = useCallback(id => {
dispatch({
type: 'REMOVE_USER',
id
});
}, []);
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onToggle={onToggle} onRemove={onRemove} />
<div>ํ์ฑ์ฌ์ฉ์ ์ : {count}</div>
</>
);
}
export default App;
setter ํจ์๊ฐ ํ๊ฐ๋ฉด useState
setter ํจ์๊ฐ ๋๊ฐ ์ด์์ด๋ฉด useReducer ๊ฐ ์ข๋ค~
21. ์ปค์คํ Hooks ๋ง๋ค๊ธฐ => ์ปดํฌ๋ํธ ๋ก์ง ๋ถ๋ฆฌ!
<useInput.js>
import { useState, useCallback } from 'react';
function useInputs(initialForm) {
const [form, setForm] = useState(initialForm);
// change
const onChange = useCallback(e => {
const { name, value } = e.target;
setForm(form => ({ ...form, [name]: value }));
}, []);
const reset = useCallback(() => setForm(initialForm), [initialForm]);
return [form, onChange, reset];
}
export default useInputs;
<App.js>
import React, { useRef, useReducer, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
import useInputs from './hooks/useInputs';
function countActiveUsers(users) {
console.log('ํ์ฑ ์ฌ์ฉ์ ์๋ฅผ ์ธ๋์ค...');
return users.filter(user => user.active).length;
}
const initialState = {
users: [
{
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
}
]
};
function reducer(state, action) {
switch (action.type) {
case 'CREATE_USER':
return {
users: state.users.concat(action.user)
};
case 'TOGGLE_USER':
return {
users: state.users.map(user =>
user.id === action.id ? { ...user, active: !user.active } : user
)
};
case 'REMOVE_USER':
return {
users: state.users.filter(user => user.id !== action.id)
};
default:
return state;
}
}
function App() {
const [{ username, email }, onChange, reset] = useInputs({
username: '',
email: ''
});
const [state, dispatch] = useReducer(reducer, initialState);
const nextId = useRef(4);
const { users } = state;
const onCreate = useCallback(() => {
dispatch({
type: 'CREATE_USER',
user: {
id: nextId.current,
username,
email
}
});
reset();
nextId.current += 1;
}, [username, email, reset]);
const onToggle = useCallback(id => {
dispatch({
type: 'TOGGLE_USER',
id
});
}, []);
const onRemove = useCallback(id => {
dispatch({
type: 'REMOVE_USER',
id
});
}, []);
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onToggle={onToggle} onRemove={onRemove} />
<div>ํ์ฑ์ฌ์ฉ์ ์ : {count}</div>
</>
);
}
export default App;
22. Context API๋ฅผ ์ฌ์ฉํ ์ ์ญ๊ฐ ๊ด๋ฆฌ
const UserDispatch = React.createContext(null);
createContext์ ํ๋ผ๋ฏธํฐ์๋ Context์ ๊ธฐ๋ณธ๊ฐ
<UserDispatch.Provider value={dispatch}>...</UserDispatch.Provider>
Context ์์ Provider๋ผ๋ ์ปดํฌ๋ํธ๋ฅผ ํตํ์ฌ Context์ ๊ฐ์ (value๋ก) ์ ํ ์ ์๋ค.
=> Provider์ ์ํ์ฌ ๊ฐ์ธ์ง ์ปดํฌ๋ํธ ์ค ์ด๋์๋ ์ง Context์ ๊ฐ์ ๋ค๋ฅธ ๊ณณ์์ ๋ฐ๋ก ์กฐํํด์ ์ฌ์ฉํ ์ ์๋ค.
<App.js>
import React, { useRef, useReducer, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
import useInputs from './hooks/useInputs';
function countActiveUsers(users) {
console.log('ํ์ฑ ์ฌ์ฉ์ ์๋ฅผ ์ธ๋์ค...');
return users.filter(user => user.active).length;
}
const initialState = {
users: [
{
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
}
]
};
function reducer(state, action) {
switch (action.type) {
case 'CREATE_USER':
return {
users: state.users.concat(action.user)
};
case 'TOGGLE_USER':
return {
...state,
users: state.users.map(user =>
user.id === action.id ? { ...user, active: !user.active } : user
)
};
case 'REMOVE_USER':
return {
...state,
users: state.users.filter(user => user.id !== action.id)
};
default:
return state;
}
}
// UserDispatch ๋ผ๋ ์ด๋ฆ์ผ๋ก ๋ด๋ณด๋ด์ค๋๋ค.
export const UserDispatch = React.createContext(null);
function App() {
const [{ username, email }, onChange, onReset] = useInputs({
username: '',
email: ''
});
const [state, dispatch] = useReducer(reducer, initialState);
const nextId = useRef(4);
const { users } = state;
const onCreate = useCallback(() => {
dispatch({
type: 'CREATE_USER',
user: {
id: nextId.current,
username,
email
}
});
onReset();
nextId.current += 1;
}, [username, email, onReset]);
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<UserDispatch.Provider value={dispatch}>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} />
<div>ํ์ฑ์ฌ์ฉ์ ์ : {count}</div>
</UserDispatch.Provider>
);
}
export default App;
<UserList.js> => onToggle/onRemove ๊ด๋ จ ์ฝ๋ ์ญ์
import React, { useContext } from 'react';
import { UserDispatch } from './App';
const User = React.memo(function User({ user }) {
const dispatch = useContext(UserDispatch);
return (
<div>
<b
style={{
cursor: 'pointer',
color: user.active ? 'green' : 'black'
}}
onClick={() => {
dispatch({ type: 'TOGGLE_USER', id: user.id });
}}
>
{user.username}
</b>
<span>({user.email})</span>
<button
onClick={() => {
dispatch({ type: 'REMOVE_USER', id: user.id });
}}
>
์ญ์
</button>
</div>
);
});
function UserList({ users }) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} />
))}
</div>
);
}
export default React.memo(UserList);
23. Immer๋ฅผ ์ฌ์ฉํ ๋ ์ฌ์ด ๋ถ๋ณ์ฑ ๊ด๋ฆฌ => ๋ฐฐ์ด ๊ฐ์ฒด๊ฐ ๊น์ ๊ณณ(2deps ์ด์)์ ์์นํ ๋ ์ฌ์ฉํ์
์ํ๋ฅผ ์ ๋ฐ์ดํธํ ๋, ๋ถ๋ณ์ฑ์ ์ ๊ฒฝ์ฐ์ง ์์ผ๋ฉด์ ์ ๋ฐ์ดํธ๋ฅผ ํด์ฃผ๋ฉด Immer๊ฐ ๋ถ๋ณ์ฑ ๊ด๋ฆฌ๋ฅผ ๋์ ํด์ค๋ค.
$ yarn add immer
import produce from 'immer';
produce ํจ์์ ์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์๋ ์์ ํ๊ณ ์ถ์ ์ํ, ๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ์๋ ์ด๋ป๊ฒ ์ ๋ฐ์ดํธํ๊ณ ์ถ์์ง ์ ์ํ๋ ํจ์๋ฅผ ๋ฃ์ด์ค๋ค. (๋ถ๋ณ์ฑ ์ ๊ฒฝ X)
<์์>
const state = {
number: 1,
dontChangeMe: 2
};
const nextState = produce(state, draft => {
draft.number += 1;
});
console.log(nextState);
// { number: 2, dontChangeMe: 2 }
<App.js>
import React, { useReducer, useMemo } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
import produce from 'immer';
function countActiveUsers(users) {
console.log('ํ์ฑ ์ฌ์ฉ์ ์๋ฅผ ์ธ๋์ค...');
return users.filter(user => user.active).length;
}
const initialState = {
users: [
{
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
}
]
};
function reducer(state, action) {
switch (action.type) {
case 'CREATE_USER':
return produce(state, draft => {
draft.users.push(action.user);
});
case 'TOGGLE_USER':
return produce(state, draft => {
const user = draft.users.find(user => user.id === action.id);
user.active = !user.active;
});
case 'REMOVE_USER':
return produce(state, draft => {
const index = draft.users.findIndex(user => user.id === action.id);
draft.users.splice(index, 1);
});
default:
return state;
}
}
// UserDispatch ๋ผ๋ ์ด๋ฆ์ผ๋ก ๋ด๋ณด๋ด์ค๋๋ค.
export const UserDispatch = React.createContext(null);
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
const { users } = state;
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<UserDispatch.Provider value={dispatch}>
<CreateUser />
<UserList users={users} />
<div>ํ์ฑ์ฌ์ฉ์ ์ : {count}</div>
</UserDispatch.Provider>
);
}
export default App;
'โ๏ธ > React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
React ๊ณต๋ถํ๊ธฐ(4) - API ์ฐ๋ํ๊ธฐ (0) | 2021.11.05 |
---|---|
React ๊ณต๋ถํ๊ธฐ(2) - styled-components (0) | 2021.10.15 |
React ๊ณต๋ถํ๊ธฐ(1) - ์ํ๊ด๋ฆฌ ไธญ ๋ฐฐ์ดํธ (0) | 2021.10.10 |
React ๊ณต๋ถํ๊ธฐ(1) - ์ํ๊ด๋ฆฌํธ (0) | 2021.10.10 |
React ๊ณต๋ถํ๊ธฐ(1) - ๊ธฐ๋ณธํธ (0) | 2021.10.10 |