
ํ
(Hook)์ ํจ์ํ ์ปดํฌ๋ํธ์ ๋ฆฌ์กํธ์ ๋ค๋ฅธ ๊ธฐ๋ฅ๋ค์ ๊ฐ๊ณ ๋ฆฌ์ฒ๋ผ ์ฐ๊ฒฐํด์ฃผ๋ ๊ฒ
Hook์ React v16.8๋ถํฐ ์ฌ์ฉ ๊ฐ๋ฅํ ์๋ก์ด ๊ธฐ๋ฅHook๋ค์ use๋ก ์์ํ๋ ํจ์๋คHook์ ํจ์ํ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ์ํธ ๋ ๋๋ง ๊ณผ์ ์์ ํญ์ ๊ฐ์ ์์๋ก ํธ์ถ๋๋ ๊ท์น์ ์ด์ฉํ์ฌ ํธ์ถ์์๋ฅผ ์ด์ฉํ์ฌ ์ํ ๋งค์นญReact์ useState()๋ฅผ ์ฌ์ฉํ๋ฉด ํจ์ํ ์ปดํฌ๋ํธ์๋ state๋ฅผ ์ค์ ํ ์ ์๋ค.
state์ state๋ฅผ ์
๋ฐ์ดํธํ๋ ํจ์๋ฅผ ์์ผ๋ก ์ ๊ณตuseState()๋ ์ ๋ฌ ๋ฐ๋ ์ธ์๋ก state์ ์ด๊ธฐ ๊ฐ์ ์ค์ (์ฒซ ๋ ๋๋ง ๋ ๋ ๋ฑ ํ๋ฒ ์ฌ์ฉ)const [state, setState] = React.useState(์ด๊ธฐ๊ฐ);
useState()๋ก ์ค์ ๋ ํจ์ํ ์ปดํฌ๋ํธ์ state๋ ํด๋์ค์ state์ ๋ฌ๋ฆฌ 1๊ฐ ์ด์์ state ๋ง๋ค ๊ฐ๋ณ์ ์
๋ฐ์ดํธ๊ฐ ์๊ตฌ๋๋ค.
๋ฐ๋ฉด ํด๋์ค ์ปดํฌ๋ํธ๋ state ์
๋ฐ์ดํธ ์, state์ ์ผ๋ถ ๋ฐ์ดํฐ๋ฅผ ๊ต์ฒดํ๊ณ ํฉ์น๋ค
useEffect() ํ
์ ํด๋์ค ์ปดํฌ๋ํธ์ ๋ค์์ ๋ผ์ดํ ์ฌ์ดํด ํ
์ ํ๋์ API๋ก ํตํฉํ ๊ฒ
useEffect => use + sideEffect??
๋ผ์ดํ์ฌ์ดํด์ ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ์ํฅ์ ์ค ์ ์๊ณ
์ปดํฌ๋ํธ ๋ ๋๋ง ๊ณผ์ ์์ ๊ตฌํ๋ ์ ์๋ ๊ฒ์ด๊ธฐ์
์ด๋ฌํ ๋์์ Side Effect(๋ถ์์ฉ)์ด๋ผ ๋ถ๋ฅธ๋ค.
| ์ฌ์ด๋ ์ดํํธ | ์ค๋ช |
|---|---|
| DOM ์ปจํธ๋กค | ์ค์ DOM ๋
ธ๋์ธ ๋ฌธ์ ์ ๋ชฉ ์์ <title>์ ์ ๊ทผ ๋ฐ ์ฝํ
์ธ ๋ณ๊ฒฝ |
| Time ์ปจํธ๋กค | ํน์ ์๊ฐ ์ดํ <title> ์ฝํ
์ธ ๋ณ๊ฒฝ |
import React, { useState, useEffect } from 'react';
function CountDown(props) {
const [count, setCount] = useState(10);
// ์ฌ์ด๋ ์ดํํธ
useEffect(() => {
// ์ค์ DOM ๋
ธ๋์ ์ ๊ทผ
const docTtile = document.querySelector('title');
// ๋ฌธ์ ์ ๋ชฉ์ ๋ฐฑ์
let originDocTitleContent = docTitle.innerText;
// ๋ฌธ์ ์ ๋ชฉ(title) ๋ณ๊ฒฝ
docTitle.innerText = `์นด์ดํธ ๋ค์ด (${count})`;
// 3์ด ๋ค, ๋ฌธ์ ์ ๋ชฉ์ ๋ณต๊ตฌ
setTimeout(() => docTitle.innerText = originDocTitleContent;
})
return (
<button type="button" onClick={() => setCount(count - 1)}>
count down ({count})
</button>
)
}
useEffect()๋ ์ ๋ฌ ๋ฐ์ ํจ์๋ฅผ DOM ์
๋ฐ์ดํธ ์ดํ ์์ ์ ์คํํ๋ค.
์ค์ ๋ ํจ์๋ ์ปดํฌ๋ํธ ๋ด๋ถ์ ์์นํด ์์ด ์ปดํฌ๋ํธ์ state, props์ ์ ๊ทผํ ์ ์๋ค.
์ปดํฌ๋ํธ ๋ ๋๋ง(componentDidMount), ์
๋ฐ์ดํธ ์ดํ(componentDidUpdate)์์ ์ ๋น ์ง์์ด ์คํ๋๋ค.
ํ์ํ ๊ฒฝ์ฐ, ์ปดํฌ๋ํธ๊ฐ ์ ๊ฑฐ ๋๊ธฐ ์ (componentWillUnmount)์ ํน์ ์ฝ๋๋ฅผ ์คํํ๋๋ก ์ค์ ๊ฐ๋ฅ
import React, { useState, useEffect } from 'react';
function CountDown(props) {
const [count, setCount] = useState(10);
useEffect(() => {
...
// ์ปดํฌ๋ํธ๊ฐ ์ ๊ฑฐ ๋ ๋ ์คํ๋ ํจ์
return () => {
clearTimeout(timeoutID):
}
});
return (...)
}
๋งค๋ฒ ๋ ๋๋ง, ์
๋ฐ์ดํธ ๊ณผ์ ์์ ์ดํํธ ํจ์๊ฐ ์คํ๋ ๊ฒฝ์ฐ ์ฑ๋ฅ์ ์
์ํฅ์ ๋ผ์น๋ค.
๋น๊ต ๊ณผ์ ์ ํตํด ๋ณ๊ฒฝ ์ฌํญ์ด ๋ฐ์ํ ๋๋ง ์ดํํธ ํจ์๊ฐ ์คํ๋๋ค๋ฉด ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
useEffect()์ ๋๋ฒ์งธ ์ธ์๋ก ๋ฐฐ์ด๊ฐ ์์ ๋น๊ตํ ๋ฐ์ดํฐ(state,props)๋ฅผ ๋ฃ๋๋ค.
๋น ๋ฐฐ์ด์ผ ๊ฒฝ์ฐ componentDidMount๋ง ์คํ (์ฒซ ๋ ๋๋ง ํ๋ฒ)
useEffect(
() => {...},
[count] // count ๊ฐ์ด ๋ณํ ๋ ๋ง๋ค ์คํ๋จ
)
ํด๋์ค ์ปดํฌ๋ํธ์ ๋ฉค๋ฒ๋ณ์๋ ์ปดํฌ๋ํธ๊ฐ ๋ง๋ค์ด์ง๋ ํ๋ฒ๋ง ๋ง๋ค์ด์ง๊ณ render()๋ง ๋ฐ๋ณต์ ์ผ๋ก ํธ์ถ๋๋ ๋ฐ๋ฉด์ ํจ์ํ ์ปดํฌ๋ํธ๋ ํจ์๋ผ ์ปดํฌ๋ํธ๊ฐ ๋ณ๊ฒฝ์ด๋๋ฉด ๋ธ๋ก ์ ์ฒด๊ฐ ๊ณ์ ๋ฐ๋ณตํด์ ํธ์ถ์ด ๋๋ค.
์ฆ, ํจ์ํ ์ปดํฌ๋ํธ๋ props๋ state๊ฐ ๋ณ๊ฒฝ์ด๋ ๋๋ง๋ค ํจ์ ์์ ์๋ ๋ณ์๋ ํจ์๋ค์ด ์๋ก์ด ๊ฐ์ผ๋ก ๋์ฒด๋จ.
๋ฐ๋ฉด hooks๋ค์ ๋ฆฌ์กํธ๊ฐ ์์์ ์๋์ผ๋ก ๊ธฐ์ตํ๋ฏ๋ก ์๋ฌด๋ฆฌ ๋ง์ด ํธ์ถํด๋ ์ฐ๊ฒฐ๋ data๋ ๋ฐ๋ก ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ๋์์ด ๊ฐ์ ์ฌ์ฌ์ฉํ๋ค.
props๋ก ๋๊ธฐ๋ ์ฝ๋ฐฑํจ์๊ฐ ์์ผ๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋ ๋๋ง๋ค ์๋ก์ด ๊ฐ์ผ๋ก ๋์ฒด๋๋ฉด์ props์ ๋ณํ๋ก re-rendering๋๋ Side Effect(๋ถ์์ฉ)์ด ์๊ธด๋ค.
์ด๋ฐ ๋ถ์์ฉ์ ๋ฐฉ์งํ๊ณ ์ ์ฌ์ฉํ๋ ๊ฒ์ดuseCallback()
useRef()๋ ์ค์ DOM ๋
ธ๋๋ฅผ ์ฐธ์กฐ(Ref.)ํ ๊ฒฝ์ฐ ์ฌ์ฉ
์ฐธ์กฐ ๋์์ ๋ณ๊ฒฝ์ด ํ์ํ ๊ฒฝ์ฐ .current ์์ฑ์ ์ฌ์ฉ
import React, { useRef } from 'react'; // import hook
const buttonRef = useRef(null); // useRef hook ์์ฑ
<button ref={buttonRef}> // useRef ์ค์
useRef()๋ฅผ ์ฌ์ฉํด ์ค์ DOM ๋ ธ๋๋ฅผ ์กฐ์ํ ๊ฒฝ์ฐ, ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๊ทธ๋ ค์ง์ง ์๋๋ค.
๋ฌด์กฑ๊ฑด state ๋๋ props๊ฐ ๋ณ๊ฒฝ์ด ๋์ด์ผ ์ ๋ฐ์ดํธ!!
๋ง์ฝ ์ค์ DOM ๋ ธ๋ ๋ณ๊ฒฝ์ ๊ฐ์งํด ํน์ ์ฝ๋๋ฅผ ์ํํ๊ณ ์ ํ๋ค๋ฉด useCallback() ํ ์ ์ฌ์ฉ/* useCallback() ํ ์ ์ฌ์ฉํด ์ฐธ์กฐ ๋ ์ค์ DOM ๋ ธ๋์ width ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด ์ปดํฌ๋ํธ state๋ฅผ ์ ๋ฐ์ดํธํ์ฌ ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๊ทธ๋ฆผ */ const [width, setWidth] = useState(0); // ์ค์ DOM ๋ ธ๋๋ฅผ ์ฝ๋ฐฑ ์ฐธ์กฐ(Callback Ref.)ํ์ฌ ๋ณ๊ฒฝ ๊ฐ์ง์ ์ปดํฌ๋ํธ ๋ฆฌ๋ ๋๋ง const domPanelEl = useCallback((node) => { if (node !== null) { setWidth(node.getBoundingClientRect().width); } }, []);
useContext()ํ
์ ์ปจํ
์คํธ ๊ฐ์ฒด์ ํ์ฌ ๊ฐ(value)์ ์๋น(Consumer)ํ ์ ์๋๋ก ์ค์ ํ๋ค.
import React, { useContext } from 'react';
import AuthContext from '../context/AuthContext';
function SignIn(props) {
const authContext = useContext(AuthContext);
const { isAuth, signIn } = authContext;
return (
{
isAuth ?
<div className="signed">๋ก๊ทธ์ธ ๋จ</div> :
<button type="button" onClick={() => signIn}>๋ก๊ทธ์ธ</button>
}
)
}
useContext() ํ ์ ํด๋์ค ์ปดํฌ๋ํธ์ static contextType ๋๋
<Context.Consumer>์ ๋์ผํ๋ฉฐ, ์ปจํ ์คํธ๋ฅผ ์ฝ๊ณ , ๋ณ๊ฒฝ์ฌํญ์ ๊ตฌ๋ ํ๋ ๊ฒ๋ง ๊ฐ๋ฅ
์ปจํ ์คํธ์ ๊ฐ(value)์ ๋ณ๊ฒฝํ๋ ค๋ฉด <Context.Provider>๊ฐ ํ์
์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ์ฌ์ฌ์ฉ ๋ ์ ์๋ ๋ก์ง๋ค์ ์ปค์คํ ํ ์ผ๋ก ์ ์ํด์ ์ฌ์ฉ
ํ ์ ๊ธฐ๋ฅ์ด๋ผ๊ธฐ ๋ณด๋ค๋ ์ปจ๋ฒค์ (Convention, ํ์)์ ๊ฐ๊น๋ค.
ํ ์use๋ก ์์ํ๋ ์ด๋ฆ์ ๊ฐ๋ ์ฝ์๋ ํจ์
// ์นด์ดํธ ๋ค์ด ์ํ๋ฅผ ๋ฐํํ๋ ์ปค์คํ
ํ
import React, { useState } from 'react';
// ์ฌ์ฉ์ ์ ์ ํ
function useCountDownStatus(count) {
const [countStatus, setCountStatus] = useState('์๋ฃ ์ ');
if (count === 0) {
setCountStatus('์๋ฃ');
}
return countStatus;
}
export default useCountDownStatus
์ ์ํ ์ปค์คํ ํ ์ ์ฌ์ฌ์ฉํ ์ ์๋ค.
import useCountDownStatus from '../hooks/useCountDownStatus';
const CountDownStatusMessage = ({ count }) => {
const countStatus = useCountDownStatus(count);
return countStatus === '์๋ฃ' ?
<div>์๋ฃ ๋จ</div> :
<div>์นด์ดํธ ๋ค์ด ์ค..</div>
}