Intro
์ค๋์ ๊ฐ๋จํ ์ผ๊ธฐ์ฅ ํ๋ก์ ํธ ๋ง์ง๋ง ๊ฐ์!!

์ค๋ ํ์ตํ ๋ด์ฉ
์ต์ ํ3 - ์ปดํฌ๋ํธ & ํจ์ ์ฌ์ฌ์ฉํ๊ธฐ
์ปดํฌ๋ํธ๋ฅผ ์ต์ ํํ๊ธฐ ์ํด์ ์ด๋ค ์ปดํฌ๋ํธ๊ฐ ์ต์ ํ ๋์์ธ์ง ์ฐพ์๋ผ ์ ์์ด์ผ ํ๋ค.
โก๏ธ Chrome ํ์ฅ ์ฑ React Developer Tools๋ฅผ ์ ํ์ฉํ์!!
useCallback
ํน์ ํจ์๋ฅผ ์๋ก ์์ฑํ์ง ์๊ณ ๋ฉ๋ชจ์ด์ ์ด์ ๋ ์ฝ๋ฐฑ์ ๋ฐํํ๋ค.
const onCreate = useCallback((author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
author,
content,
emotion,
created_date,
id: dataId.current,
};
dataId.current += 1;
// ํจ์ํ ์
๋ฐ์ดํธ: setData์ ๊ฐ์ด ์๋ ํจ์๋ฅผ ์ ๋ฌํ๋ค.
setData((data) => [newItem, ...data]);
}, []);
useMemo์ ์ฐจ์ด์
โก๏ธ useMemo๋ ์ฐ์ฐ๋ ๊ฒฐ๊ณผ๊ฐ์ ๋ฐํํ๊ณ , useCallback์ ํจ์๋ฅผ ๋ฐํํ๋ค.
์ต์ ํ4 - ํ๋ก์ ํธ ์ต์ ํ ์๋ฃ
์ต์ ํ์ ์์์ React.memo๋ก ์ปดํฌ๋ํธ๋ฅผ ๋ฌถ์ด์ฃผ๋ ๊ฒ!
import React, { ... } from 'react';
const DiaryItem() => {
...
return (...)
};
export default React.memo(DiaryItem);
๋ณต์กํ ์ํ๋ณํ ๋ก์ง ๋ถ๋ฆฌ - useReducer
dispatch๊ฐ ํธ์ถ๋๋ฉด ์ํ๋ณํ๊ฐ ์ผ์ด๋์ผ ํจ
import './App.css';
import DiaryEditor from './DiaryEditor';
import DiaryList from './DiaryList';
import { useRef, useEffect, useMemo, useCallback, useReducer } from 'react';
const reducer = (state, action) => {
switch (action.type) {
case 'INIT': {
return action.data;
}
case 'CREATE': {
const created_date = new Date().getTime();
const newItem = { ...action.data, created_date };
return [newItem, ...state];
}
case 'REMOVE': {
return state.filter((item) => item.id !== action.targetId);
}
case 'EDIT': {
return state.map((item) => (action.targetId === item.id ? { ...item, content: action.newContent } : item));
}
default:
return state;
}
};
function App() {
...
return (...)
}
export default App;
- reducer ํจ์: ํ์ฌ ์ํ(state)์ ์ก์ ๊ฐ์ฒด(action)๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ ์๋ก์ด ์ํ๋ฅผ ๋ฐํํด์ฃผ๋ ํจ์
const [data, dispatch] = useReducer(reducer, []);
- dispatch: ์ก์ ์ ๋ฐ์์ํค๋ ํจ์ ex) dispatch({ type: 'REMOVE', targetId });
- data(state): ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ ์ ์๋ ์ํ
const reducer = (state, action) => {
...
};
function App() {
...
const onCreate = useCallback((author, content, emotion) => {
dispatch({
type: 'CREATE',
data: {
author,
content,
emotion,
id: dataId.current,
},
});
dataId.current += 1;
}, []);
const onRemove = useCallback((targetId) => {
dispatch({ type: 'REMOVE', targetId });
}, []);
const onEdit = useCallback((targetId, newContent) => {
dispatch({ type: 'EDIT', targetId, newContent });
}, []);
return (...)
};
export default App;
์ปดํฌ๋ํธ ํธ๋ฆฌ์ ๋ฐ์ดํฐ ๊ณต๊ธ - Context API
props๋ฅผ ์ ๋ฌ๋ง ํ๋ ์ปดํฌ๋ํธ๊ฐ ๋ง์ด ์๊ธฐ๋ฉด (Props Drilling) ์ฝ๋ ์ ์ง๋ณด์๊ฐ ์ด๋ ค์์ง๋ค.
Context API๋ฅผ ์ฌ์ฉํด ์ปดํฌ๋ํธ ํธ๋ฆฌ ๋จ๊ณ๋ง๋ค ๋ช ์์ ์ผ๋ก props๋ฅผ ๋๊ฒจ์ฃผ์ง ์๊ณ ๊ฐ์ ๊ณต์ ํ ์ ์๋๋ก ๊ตฌํํ ์ ์๋ค.
- Context ์์ฑ
const MyContext = React.createContext(defaultValue);
- Context Provider๋ฅผ ํตํ ๋ฐ์ดํฐ ๊ณต๊ธ
<MyContext.Provider value={์ ์ญ์ผ๋ก ์ ๋ฌํ๊ณ ์ํ๋ ๊ฐ}>
// ์ด Context์์ ์์นํ ์์ ์ปดํฌ๋ํธ๋ค
</MyContext.Provider>
return (
<DiaryStateContext.Provider value={data}>
<DiaryDispatchContext.Provider value={memoizedDispatches}>
<div className="App">
<DiaryEditor />
<div>์ ์ฒด ์ผ๊ธฐ: {data.length}</div>
<div>๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ ๊ฐ์: {goodCount}</div>
<div>๊ธฐ๋ถ ๋์ ์ผ๊ธฐ ๊ฐ์: {badCount}</div>
<div>๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ ๋น์จ: {goodRatio}%</div>
<DiaryList />
</div>
</DiaryDispatchContext.Provider>
</DiaryStateContext.Provider>
);
- ๋ฐ์ดํฐ state๊ฐ ๋ฐ๋ ๋๋ง๋ค ๋ ๋๋ง๋๊ธฐ ๋๋ฌธ์ ๋ ๋๋ง์ด ๋ถํ์ํ ๊ฐ์ ๊ตณ์ด ํจ๊ป ์ฐ์ง ์๊ณ ์๋ก์ด Context๋ฅผ ์ฌ์ฉํด ๋ถ๋ฆฌํ ์ ์๋ค. (๊ฐ์ ์ ํ x)
export const DiaryDispatchContext = React.createContext();
const onCreate = useCallback((author, content, emotion) => {
dispatch({
type: 'CREATE',
data: {
author,
content,
emotion,
id: dataId.current,
},
});
dataId.current += 1;
}, []);
const onRemove = useCallback((targetId) => {
dispatch({ type: 'REMOVE', targetId });
}, []);
const onEdit = useCallback((targetId, newContent) => {
dispatch({ type: 'EDIT', targetId, newContent });
}, []);
/* useMemo๋ฅผ ์ฌ์ฉํ๋ ์ด์ */
/* App์ปดํฌ๋ํธ๊ฐ ์ฌ์์ฑ์ด ๋ ๋, ๊ฐ์ฒด๋ค๋ ์ฌ์์ฑ์ด ๋๋ฏ๋ก ์ฌ์์ฑ๋์ง ์๊ฒ useMemo ๊ฐ์ฒด๋ก ๋ฌถ์ด์ค์ผ ํ๋ค. */
const memoizedDispatches = useMemo(() => {
return { onCreate, onRemove, onEdit };
}, []);
- ์ฌ์ฉ๋ฐฉ๋ฒ - useContext()
import React, { useState, useRef, useContext } from 'react';
import { DiaryDispatchContext } from './App';
const DiaryItem = () => {
...
const { onEdit, onRemove } = useContext(DiaryDispatchContext);
return (...)
};
export default React.memo(DiaryItem);
๋ง๋ฌด๋ฆฌ
useContext๋ฅผ ๋ง์ง๋ง์ผ๋ก ๊ฐ๋จํ ์ผ๊ธฐ์ฅ ํ๋ก์ ํธ๋ฅผ ๋ง๋ฌด๋ฆฌํ๋ค.
๋ฆฌ์กํธ์ ๋ํด ์ด๋์ ๋ ๊ฐ์ด ์กํ ๊ฒ ๊ฐ๋ค.
๋ฐฐ์ด ๊ฒ์ด ๋ง์์ ๋ณต์ต์ ์ฒ ์ ํ ํด์ผ๊ฒ ๋ค.