- useState
- useEffect
- useContext
- useReducer
- useCallback
- useMemo
- useRef
- useImperativeHandle
- useLayoutEffect
- useDebugValue
const [state, setState] = useState(initialState);์ฒซ๋ฒ์งธ ์ธ์๋ก ์ด๊ธฐ๊ฐ ์ ๋ฃ์ด์ค๋ค. useState ๋ ์ฐ๋ฆฌ๊ฐ ๋ง์ด ์ฌ์ฉํ๋ state ์ด๊ธฐํ ์์
๊ณผ ๋์ผํ๋ค๊ณ ์๊ฐํ๋ฉด ์ฝ๋ค. ์์ฑ์ ์ฐ๋ฆฌ๋ ๋ฐฐ์ด์ ๋ฐ์ ์ฌ ์ ์๋ค.
๋ชจ๋ Hook์ 2๊ฐ์ ์ธ์๋ฅผ ๋ฐฐ์ด๋ก ๋ฐ์์จ๋ค.(๋๋ฌผ๊ฒ ์๋ ๊ฒ๋ ์๋ค.)
๋ฐฐ์ด์ ์ฒซ๋ฒ์งธ๋ ์ฐ๋ฆฌ๊ฐ ๋๊ฒจ์ฃผ์๋ ์ด๊ธฐํ ๊ฐ์ด ๋ค์ด์ค๊ณ ์ดํ์๋ ๋ณ๊ฒฝ๋ ๊ฐ์ด ๋์ด์ค๊ฒ ๋๋ค. ๋๋ฒ์งธ๋ ๊ฐ๋ฅผ ๋ณ๊ฒฝํ๋ ํจ์๋ค.
re-render๊ฐ ๋ ๊ฒฝ์ฐ useState์ ์ํด ๋ฐํ๋ ์ฒซ ๋ฒ์งธ ๊ฐ์ ํญ์ ์ ๋ฐ์ดํธ๋ฅผ ์ ์ฉํ ํ ๊ฐ์ฅ ์ต๊ทผ ์ํ๊ฐ ๋๋ค.
์ญ์๋ ์ฐ๋ฆฌ๋ ์ธ์๋ก ํจ์๋ฅผ ๋ณด๋ผ์ ์๋ค. ๊ทธ๋ฌ๋ ๊ธฐ์กด์ ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ๋ setState ๋ฉ์๋์๋ ๋ฌ๋ผ์ ์๋์ผ๋ก ๋ณํฉ์ ์์ผ์ฃผ์ง ์๋๋ค.
์ฆ ์ฐ๋ฆฌ๊ฐ ๋ง๋ค๋ ๋ง๋ค ํ์์ ๋ฐ๋ผ ๋ณํฉ์ ํด์ค์ผํ๋ค.
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues}; // ๊ฐ๋จํ๊ฒ spread๋ฅผ ์ฌ์ฉํด์ ์๋ก ์์ฑ
});initial state ์ ์ญ์ ํจ์๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ๋ฆฌ์กํธ ์ฌ์ดํธ์์ ํํ์ผ๋ก๋ ๊ฐ ๋น์ผ ๊ณ์ฐ ์ด๋ผ๋ฉด ํจ์๋ฅผ ์ฌ์ฉํด์ ํํํ๋ผ๊ณ ํ๋ค.
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});๊ฐ์ ๊ฐ์ ๋ค์ ์
๋ฐ์ดํธ๋ฅผ ํ๋๋ผ๋ ๋ฆฌ์กํธ์์๋ ์์์ re-render ํ๊ฑฐ๋ effect ๋ฅผ ๋ฐ์์ํค์ง ์๋๋ค.(Object.is๋ฅผ ์ฌ์ฉํด์ ๋น๊ตํ๋ค๊ณ ํจ_์ฝ์ผ๋ฉด ์ ์ฉํจ)
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋ ๋ ๋๋ง์ด ์๋ฃ๋ ํ์ ์คํ์ด ๋๋ค.
์ด๋ค value ๊ฐ ๋ฐ๋์์๋ ์คํ๋๋๋ก ํ ์ ์๋ค.(๋ด๊ฐ ๋ฐ๋ผ๋ณด๊ณ ์ถ์ ์ธ์๋ฅผ ๋ฃ์ ์ ์๋ค.)
ํ๋ฉด์ ๋ ๋๊ธฐ ์ ์ ์ ๋ฆฌ๋ฅผ ํ ๋ ์ฌ์ฉํ ์ ์๋ค.(dispose()์ ๊ฐ์ ๋๋์)
์๋์ ์์๋ฅผ ๋ณด๊ฒ ๋๋ฉด return์ผ๋ก ๋ง๋ ํจ์๊ฐ ์ ๋ฆฌ๋ฅผ ํ๋ ํจ์์ด๋ค.
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// Clean up the subscription
subscription.unsubscribe();
};
});๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ์ ๋ฐฉ์งํํ๊ธฐ ์ํด ์ปดํฌ๋ํธ๊ฐ UI๋ก ๋ถํฐ ์ ๊ฑฐ๋๊ธฐ ์ ์ ์คํํ๋ค.
๋ง์ฝ์ ๊ตฌ๋ ์ ์ทจ์ํ๊ธฐ์ ์ ๋ค๋ฅธ effect๊ฐ ์คํ์ด ๋์ด ๊ตฌ๋ ์ ํ๋ค๋ฉด? ์ด๋ฌํ ์ผ์ด ๋ฐ์ํ๋ ๊ฒ์ ๋ง๊ณ ์ถ๋ค๋ฉด?
componentDidMount ๋ componentDidUpdate ๊ณผ๋ ๋ค๋ฅด๊ฒ layout๊ณผ paint ๊ฐ ๋๋๊ณ ๋์ useEffect๊ฐ ์คํ์ด๋๋ค. ์ด๋ ๊ฒ ๋๋ ๋ง๋ ๊ณตํต์ ๋ถ์์ฉ์ ์์ด์ ์ ํฉํ๋ค ๋ผ๊ณ ํ๋๋ฐ, ํ๋ฉด์์ ์
๋ฐ์ดํธ๋๋ ๊ฒ์ด ๋ฉ์ถ๋ ํ์์ด ์ผ์ด๋์ง ์์ผ๋๊น ๊ทธ๋ฐ ๊ฒ์ด๋ค.
์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ ๋ณผ ์์๋ DOM ๋ณ์ด๋ ์๊ฐ์ ์ธ ๋ถ์ผ์น๋ฅผ ์ธ์ํ์ง ๋ชปํ๋๋ก ๋ค์ ํ์ธํธ ์ ์ ๋๊ธฐ์ ์ผ๋ก ์คํํด์ผ ํ๋ค.
useLayoutEffect ๋ useEffect ๋์ผํ ๊ธฐ๋ฅ์ด์ง๋ง ์คํ๋๋ ์์ ์ด ๋ค๋ฅธ Hook ์ด๋ค. ์ฝ๊ฒ ๋งํจ๋ฉด ์คํ๋๋ ์์ ์ด ๋ค๋ฅด๋ค๋ ๊ฒ์ธ๋ฐ useEffect ๋ ์๋์ ์งํ ๊ธ์จ์ ๊ฐ์ด ์์ ํ ํ๋ฉด์ด ๊ทธ๋ ค์ก๋ค ๋ผ๋ ๊ฒ์ ๋ณด์ฅํ ์ ์์ง๋ง useLayoutEffect๋ ์์ ์ด Dom์ ๋ณํ๋ ์์ง๋ง repaint, reflow๊ฐ ๋์ง ์์ ์์ ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
useEffect๋ ๋ธ๋ผ์ฐ์ ๊ฐ paintํ ๋ ๊น์ง ์ฐ๊ธฐ๋ฅผ ํ์ง๋ง ์๋ก์ด ๋ ๋๋ง์ ์ ์์๋ ๊ฒ์ด ๋ณด์ฆํ๋ค.
useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source],
);useEffect ์๋ 2๋ฒ์งธ ์ธ์๊ฐ ์กด์ฌํ๋ค. ์ฌ๊ธฐ์ ๋ค์ด๊ฐ๋ ๋ฐฐ์ด์ ๋ด๊ฐ ๋ฐ๋ผ๋ณผ ๊ฒ ์ผ๋ก ์ฌ๊ธฐ์๋ props.source ์ ๋ณ๊ฒฝ์ด ์ผ์ด๋ฌ์ ๊ฒฝ์ฐ์๋ง ์๋ํ๊ฒ ๋๋ค.
๋ง์ฝ์ ์ธ์์ ์๋ฌด๊ฒ๋ ์๋ค๋ฉด ๋ง์ดํธ์์ ์คํ๋๊ณ ๋ง์ดํธ ํด์ ์์๋ ์ ๋ฆฌํ๋ค.
const context = useContext(Context);react@16.3๋ถํฐ ์ง์์ ํ Context ๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ์ง์ํ๋ ๊ฒ์ด๋ค. context object ๋ฅผ ๋ฐ์์ ํ์ฌ context ๊ฐ์ ๋ฐํํ๋ค.
provider๊ฐ ์
๋ฐ์ดํธ๋๋ฉด Hooks์ด ์คํ๋์ด ์ต์ ์ ๊ฐ์ผ๋ก ๋น์ฐํ ๋๊ธฐํ๊ฐ ๋๋ค.
์ฐ๋ฆฌ๊ฐ ๋ฆฌ๋์ค์์ ๋ง์ด ๋ณด๋ Reducer ์ ๋น์ทํ๋ค๊ณ ์๊ฐํ๋ฉด ๋๋ค. ๊ทธ๋ ๋ค๊ณ ํ๋ค.
const initialState = {count: 0};
// ์ฐ๋ฆฌ๊ฐ Redux์์ ๋ง๋ค๋ Reducer ๋ถ๋ถ
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter({initialCount}) {
// ์ด๊ธฐํ ๊ฐ๊ณผ Reducer๋ฅผ ๋ฃ์ด์ค๋ค.
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
</>
);
}2๋ฒ์งธ ์ธ์๋ก ์ด๊ธฐํ state ๋ฅผ ๋๊ฒจ์ค๋ค.(๊ฐ๋จํ๊ฒ ์ด๊ธฐํํ๋ ๋ฐฉ๋ฒ)
const [state, dispatch] = useReducer(
reducer,
{count: initialCount}
);๋ง์ฝ ๋์จํ๊ฒ ์ด๊ธฐ state ๋ฅผ ๋ง๋ค๊ณ ์ถ๋ค๋ฉด 3๋ฒ์งธ ์ธ์๋ก init function ์ ๋๊ธฐ์
๊ทธ๋ฌ๋ฉด init(initialArg) ์ด๋ฐ์์ผ๋ก ํธ์ถ์ด ๋ ๊ฒ์ด๋ค.
function init(initialCount) {
return {count: initialCount};
}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return init(action.payload);
default:
throw new Error();
}
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
<>
Count: {state.count}
<button
onClick={() => dispatch({type: 'reset', payload: initialCount})}>
Reset
</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
</>
);
}useState ์ ๋์ผํ๊ฒ ๊ฐ์ ๊ฐ์ ๋๊ธฐ๊ฒ ๋๋ฉด ๋ด๋ถ์ ์ผ๋ก ๋น๊ต๋ฅผ ํด์ ์์ ๋ ๋๋ง๊ณผ effect๋ฅผ ์คํํ๋ ๊ฒ์ ๋ง๋๋ค.
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);Returns a memoized callback.
์ธ๋ผ์ธ ์ฝ๋ฐฑ๊ณผ ์
๋ ฅ ๋ฐฐ์ด์ ์ ๋ฌํฉ๋๋ค. useCallback ์ ์
๋ ฅ ์ค ํ๋๊ฐ ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ์๋ง ๋ณ๊ฒฝ๋๋ ์ฝ๋ฐฑ์ memoized ๋ฒ์ ์ ๋ฐํํฉ๋๋ค
์ฆ a ๋๋ b ๊ฐ ๋ณ๊ฒฝ์ด ๋์๋ค๋ฉด ํจ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
- ๋์ผํ ํํ
useCallback(fn, inputs) //is equivalent to
useMemo(() => fn, inputs). const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);Returns a memoized value.
๋ ๋๋ง๋๋ ๋์ ์ useMemo์ ์ ๋ฌ๋ ํจ์๊ฐ ์คํ์ด ๋๋ค.
์ฆ, ์ฌ์ฉํ๋๋ฐ ์์ด์ ์กฐ์ฌํด์ผํ๋ค. ๋ ๋๋ง๋์ ํ์ง์๋ ๊ฒ์ ํ์ง ์์์ผํ๋ค. side effect๋ฅผ ์ํด useEffect๋ฅผ ์ฌ์ฉํด์ผํ๋ ๊ฒ์ด๋ค.
useEffect ์ ๋์ผํ๊ฒ ๋ฐฐ์ด์ด ๋์ด๊ฐ์ง ์์ ๊ฒฝ์ฐ ๋งค์๊ฐ ์ฒซ๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ฅผ ํ๊ฒ ๋๋ค.
์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํด์ useMemo๋ฅผ ์ฌ์ฉํ ์ ์์ ๊ฒ์ผ๋ก ์๊ฐ์ด ๋๋ค.
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>);
} useImperativeHandle(ref, createHandle, [inputs])useImperativeHandle ์ ref ๋ฅผ ์ฌ์ฉํ ๋ ๋ถ๋ชจ ๊ตฌ์ฑ ์์์ ๋
ธ์ถ๋๋ ์ธ์คํด์ค ๊ฐ์ ์ฌ์ฉ์ ์ ์ํฉ๋๋ค.
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);์๋ช
์ useEffect์ ๋์ผํ์ง๋ง ๋ชจ๋ DOM ๋ณ์ด ํ์ ๋๊ธฐ์ ์ผ๋ก ์์ ๋ฉ๋๋ค.
useLayoutEffect ๋ด๋ถ์์ ์์ฝ๋ ์
๋ฐ์ดํธ๋ ๋ธ๋ผ์ฐ์ ๊ฐ ํ์ธํธํ ์ ์๊ธฐ ์ ์ ๋๊ธฐ์ ์ผ๋ก ํ๋ฌ์๋ฉ๋๋ค.
์ด๊ฒ์ ์ฌ์ฉํ์ฌ DOM์์ ๋ ์ด์์์ ์ฝ๊ณ ๋๊ธฐ์ ์ผ๋ก ๋ค์ ๋ ๋๋งํฉ๋๋ค.
์๊ฐ์ ์ธ ์
๋ฐ์ดํธ๋ฅผ ๋ง์ง ์๋๋ก useEffect๋ฅผ ์ฌ์ฉํ๊ธฐ๋ฅผ ๊ถ์ฅํจ
useDebugValue๋ React DevTools์์ ์ฌ์ฉ์ ์ ์ ํํฌ ๋ ์ด๋ธ์ ํ์ํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค.
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// ...
// Show a label in DevTools next to this Hook
// e.g. "FriendStatus: Online"
useDebugValue(isOnline ? 'Online' : 'Offline');
return isOnline;
} useDebugValue(date, date => date.toDateString());