ํ
(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>
}