ํจ์ํ ์ปดํฌ๋ํธ์์ ํด๋์ค์ ๋ผ์ดํ์ฌ์ดํด ๋ฉ์๋ ๊ธฐ๋ฅ๊ณผ state ๊ด๋ฆฌ๋ฅผ ํ ์ ์๊ฒ ํด์ฃผ๋ ๊ธฐ๋ฅ์ด๋ค.
๐ค Hook์ด ๋ง๋ค์ด์ง๊ฒ๋ ๊ณ๊ธฐ๋?
๊ธฐ์กด์ ๋ด๊ฐ ์ฌ์ฉํ๊ณ ์ถ์ state๋ฅผ ์ฌ๋ฌ ์ปดํฌ๋ํธ์ ์ฐ๋ ค๋ฉด props๋ก ์ ๋ฌํ์๋ค.
ํ์ง๋ง ์ปดํฌ๋ํธ๋ง๋ค state๋ฅผ ์ ๋ฌํ๋ ค๋ฉด ๋ถ๋ชจ์ปดํฌ๋ํธ๋ง๋ค ์ผ์ผํ props๋ก state๋ฅผ ์ ๋ฌํด์ค์ผํ๋ค๋ ๋ฒ๊ฑฐ๋ก์์ด ์๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด HOCs(Higher Order Components) ๋๋ render props๋ฅผ ์ ์ํ์๋ค.
1. ์ํ ๋ก์ง ์ฌ์ฌ์ฉ์ ์ด๋ ค์
์์์ ์ค๋ช ํ๋ฏ์ด state๋ฅผ ์ฌ๋ฌ ์ปดํฌ๋ํธ ์ฌ์ด์์ ๊ณต์ ํ๊ธฐ ์ํด์๋ render props์ด๋ HOCs ๋ก์ง ํจํด์ ์ฌ์ฉํ๋ค.
์ด๋ state sharing์ ํจ์จ์ ์ผ๋ก ํ ์ ์๋ค๋ ์ฅ์ ์ ๊ฐ์ง๊ณ ์์ง๋ง ๋ก์ง์ ๊ตฌ์ฑํ๊ธฐ ์ํ์ฌ ์ฝ๋์ ๋ณต์ก์ฑ์ ์ ๋ฐํ์ฌ Wrapper Hell์ ๋ฐ์์ํฌ์ ์๊ณ ์ด๋ก ์ธํ์ฌ ์ฝ๋ ์ถ์ ์ด ์ด๋ ค์์ง ์ ์๋ค๋ ์ ์ง๋ณด์ ๋ฐ ๊ฐ๋ ์ฑ์์์ ๋จ์ ์ ๊ฐ์ง๊ณ ์๋ค.
Hook์ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ๋ก๋ถํฐ ์ํ ๊ด๋ จ ๋ก์ง์ ์ถ์ํํ ์ ์๋ค.
Hook์ ๊ณ์ธต์ ๋ณํ ์์ด ์ํ ๊ด๋ จ ๋ก์ง์ ์ฌ์ฌ์ฉ ํ ์ ์๋๋ก ๋์์ค๋ค. ์ด๊ฒ์ ๋ง์ ์ปดํฌ๋ํธ ํน์ ์ปค๋ฎค๋ํฐ ์ฌ์ด์์ Hook์ ๊ณต์ ํ๊ธฐ ์ฝ๊ฒ ๋ง๋ค์ด์ค๋ค.
2. ์ปดํฌ๋ํธ์ ๋ณต์ก์ฑ
ํด๋์คํ ์ปดํฌ๋ํธ์์ ๋น์ทํ ๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋์์ ๋์ผํ ์์
์ ๋ฐ๋ณตํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค.
(์๋ฅผ ๋ค๋ฉด, ComponentDidmount,ComponentDidUpdate์์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ์์
)
์ด๋ ๊ฐ์ ์์ ์ ๋ฐ๋ณตํ๋ ๋ฒ๊ฑฐ๋ก์์ ๋ฐ์์์ผ ๋ถํธํจ์ ์ค๋ค.
์ด๋ฌํ ๋จ์ ์ ๋ณด์ํ๊ธฐ ์ํ์ฌ Hook์ ๋น์ทํ ๊ธฐ๋ฅ์ ํ๋ ๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋๋ฅผ ํ๋์ Hook API์์ ํด๊ฒฐํ ์ ์๋๋ก ํ๋ค.(ex. useEffect)
3.ํด๋์ค์ ์ด๋ ค์
state๋ฅผ ์ ์ธํด์ฃผ๊ธฐ ์ํ์ฌ super๋ก props๋ฅผ ์ ๋ฌ๋ฐ๊ณ constructor ๋ฉ์๋์์์ state๋ฅผ ์ ์ธํ๊ณ (๋ฐ์์ ๊ฐ๋จํ ํด๊ฒฐํ ์๋ ์๊ธด ํจ.) ์ด๋ ํ ๊ธฐ๋ฅ์ ํ๊ธฐ ์ํ ๋ฉ์๋๋ฅผ ์ ์ํ์ฌ ์ฌ์ฉํ ๋์๋ this๋ก ๋ฐ์ธ๋ฉํด์ค์ผํ๋ค.(์ด ๋ํ ํด๋์ค ํ๋๋ก ์ฌ์ฉํ๋ฉด ๋๊ธด ํจ.)
Hook์ ์ด๋ฌํ ๋ณต์กํ this์ ๋ํ ์ฌ์ฉ์ ํ์ง ์๊ณ ํจ์ํ ์ปดํฌ๋ํธ์์ ๊ตฌํํจ์ผ๋ก์จ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค.
useState์ ํจ์ํ ์ปดํฌ๋ํธ์์ state๋ฅผ ์ฌ์ฉํ๊ณ ๊ด๋ฆฌํ ์ ์๊ฒ ํด์ฃผ๋ Hook API์ด๋ค.
useState๋ ์ํ๊ฐ๊ณผ ์ํ๊ฐ์ ๋ณ๊ฒฝํด์ฃผ๋ ํจ์๋ฅผ ๋ด๋ ๋ฐฐ์ด์ ๋ฐํํ๊ณ
๋ช
์นญ์ ๊ตฌ์กฐ๋ถํดํจ์ผ๋ก์จ ๋ช
๋ช
ํ๋ค.
useState์ ์ธ์๋ก์จ๋ state์ ์ด๊น๊ฐ์ ํ ๋นํ๋ค.
App.js
import React, { useState } from "react";
const App = () => {
const [count,setCount] = useState(0) // ์ํ๋ ์ด๋ฆ ๋ช
๋ช
๋ฐ ์ด๊น๊ฐ ์ค์
console.log(useState(0)); // state์ state๋ฅผ ๋ณ๊ฒฝํด์ฃผ๋ ํจ์ ๋ฐํ
return <div></div>;
};
export default App;
useState(0)์ ๋ฐํ๊ฐ
์ผ๋ฐ์ ์ผ๋ก ์ผ๋ฐ ๋ณ์๋ ํจ์๊ฐ ๋๋ ๋ ์ฌ๋ผ์ง์ง๋ง, state ๋ณ์๋ React์ ์ํด ์ฌ๋ผ์ง์ง ์๋๋ค.
์ฌ๋ฌ๊ฐ์ state๋ฅผ ์ค์ ํ์ฌ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด ์ฌ๋ฌ๊ฐ์ useState๋ฅผ ์ ์ธํ๋ฉด ๋๋ค.
const [state,setState] = useState(initValue)
setState()๊ฐ ์ด์ state๊ฐ์ ์์กดํ์ฌ ๋ค์ state๊ฐ์ ์ค์ ํ๊ณ ์ ํ๋ค๋ฉด setState()์ ํจ์๋ฅผ ํ ๋นํ ์ ์๋ค.
setState( prevstate => prevstate + 1)
์ด์ state๊ฐ์ ์ฐธ์กฐํ์ฌ state๊ฐ์ ์ค์ ํด์ผํ ๋ ์ ์ฉํ ๊ฒ ๊ฐ๋ค.
๋ํ ํจ์ํ ์ ๋ฐ์ดํธ ๊ฐ์ ๊ฒฝ์ฐ์๋ setState๋ก ๋ณํ๊ฐ ์๋ค๋ฉด re-rendering์ ํด์ฃผ์ง ์๋๋ค.
useEffect๋ ํจ์ํ ์ปดํฌ๋ํธ์์ ํด๋์คํ ์ปดํฌ๋ํธ์ ์๋ช ์ฃผ๊ธฐ ๋ฉ์๋์ ๋น์ทํ๊ฒ ๊ตฌํํ ์ ์๋ Hook API์ด๋ค.
์๋ช ์ฃผ๊ธฐ ๋ฉ์๋ componentDidMount๋ componentDidUpdate, componentWillUnmount์ ๊ฐ์ ๋ชฉ์ ์ผ๋ก ์ ๊ณต๋์ง๋ง, ํ๋์ API๋ก ํตํฉํฉ๋ผ์๋ค.
๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ , ๊ตฌ๋
์ค์ , DOM ๋ค๋ฃจ๊ธฐ ๋ฑ๊ณผ ๊ฐ์ effect๋ค์ useEffect์์ ์ค์ ํ๋ค.
(DOM ๋ณ๊ฒฝ์ ํ์ด๋จธ ์ค์ ํน์ useLayoutEffectํ์ฉ)
์ฒซ๋ฒ์งธ ์ธ์์ธ ํจ์์ ์ญํ ์ effect์ด๋ค.
effect๋ฅผ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ํจ์์ ๋ฌธ์ ์ด๊ธฐ๋ ๋๋ง(componentDidMount), ์
๋ฐ์ดํธ ๋ ๋๋ง(componentDidUpdate) ์ดํ์ ์ํ๋ ์์
์ ์ค์ ํ๋ฉด ๋๋ค.
์ค์ ํ ์์
(effect)์ ํด์งํ๋ ๋ฐฉ๋ฒ์ ํจ์์์ ํด์งํ๋ ํจ์๋ฅผ ๋ฐํํ๋ฉด ๋๋ค.(componentWillUnmount)
useEffect์ clean-up function(return์์ ์ค์ ํ๋ ํจ์)์ unmount๋ ๋๋ง ๋ถ๋ฆฌ๋ ๊ฒ์ด ์๋๋ค.
clean-up function์ ์ปดํฌ๋ํธ๊ฐ ์
๋ฐ์ดํธ ๋๊ธฐ์ ์๋ ๋ถ๋ฆฐ๋ค.
๋๋ฒ์งธ ์ธ์๋ dependency์ด๋ค.
๋๋ฒ์งธ ์ธ์๋ ๋ฐฐ์ด์ ๋ฃ์ด์ฃผ๋๋ฐ ๋ฐฐ์ด์์๋ ํด๋น๊ฐ์ด ๋ฐ๋ ๋๋ง effect๊ฐ ์ผ์ด๋๋๋ก ํ๊ฒ ํด์ค๋ค.
๋น ๋ฐฐ์ด์ ๋ฃ๋๋ค๋ฉด ์ฒซ ๋ ๋๋ง(componentDidMount)๋๋ง effect๊ฐ ์ผ์ด๋๊ฒ ํด์ค๋ค.
์๋ก ๊ด๋ จ์๋ ๋ก์ง์ useEffect๋ฅผ ์ฌ๋ฌ๋ฒ ์ฌ์ฉํ์ฌ ๊ตฌ๋ถํ ์ ์๋ค.
(ํด๋์คํ ์ปดํฌ๋ํธ์์๋ ์๋ช
์ฃผ๊ธฐ ๋ฉ์๋์์ ๊ด๋ จ์๋ ๋ก์ง์ ๋ชฐ์๋ฃ๊ณค ํด์ผํ์.)
useEffect์์์ state๋ฅผ ๋ณ๊ฒฝํ๋ฉด ๋ฌดํ๋ ๋๋ง๋ฃจํ์ ๋น ์ง ์ ์์ผ๋ ์ฃผ์ํ ๊ฒ!
(state๋ ๋ฐ๋๋๋ง๋ค ๋ฆฌ๋ ๋๋ง๋๋๋ฐ ๋ ๋๋ง๋ ๋๋ง๋ค useEffect๋ ์คํ๋์ด ๋ฌดํ๋ฃจํ์ ๋น ์ง๊ฒ ๋จ.)
useReducer๋ useState๋ณด๋ค ๋ ๋ค์ํ ์ปดํฌ๋ํธ ์ํฉ์ ๋ฐ๋ผ ๋ค์ํ ์ํ๋ฅผ ๋ค๋ฅธ ๊ฐ์ผ๋ก ์๋ฐ์ดํธํด ์ฃผ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ ํ ์ด๋ค.
๋ฆฌ๋์๋ ํ์ฌ ์ํ, ๊ทธ๋ฆฌ๊ณ ์ ๋ฐ์ดํธ๋ฅผ ์ํด ํ์ํ ์ ๋ณด๋ฅผ ๋ด์ ์ก์ ๊ฐ์ ์ ๋ฌ๋ฐ์ ์๋ก์ด ์ํ๋ฅผ ๋ฐํํ๋ ํจ์์ด๋ค.
๋ฆฌ๋์ ํจ์์์ ์๋ก์ด ์ํ๋ฅผ ๋ง๋ค ๋๋ ๋ฐ๋์ ๋ถ๋ณ์ฑ์ ์ง์ผ์ฃผ์ด์ผ ํ๋ค.
useReducer์ ์ฒซ ๋ฒ์งธ ํ๋ฆฌ๋ฏธํฐ์๋ ๋ฆฌ๋์ ํจ์๋ฅผ ๋ฃ๊ณ , ๋ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์๋ ํด๋น ๋ฆฌ๋์์ ๊ธฐ๋ณธ๊ฐ์ ๋ฃ์ด์ค๋ค.
const [state,dispatch] = useReducer(reducer,0)
์ด ํ ์ ์ฌ์ฉํ๋ฉด state ๊ฐ๊ณผ dispatch ํจ์๋ฅผ ๋ฐ์์จ๋ค. ์ฌ๊ธฐ์ state๋ ํ์ฌ ๊ฐ๋ฆฌํค๊ณ ์๋ ์ํ๊ณ , dispatch๋ ์ก์ ์ ๋ฐ์์ํค๋ ํจ์์ด๋ค.
dispatch(action)
state,action์ ๊ฐ์ฒด๋ก ์ฌ์ฉํ ๋
import React, { useReducer } from "react";
const App = () => {
function reducer(state, action) {
switch (action.type) {
case "INCREMENT":
return { value: state.value + 1 };
case "DECREMENT":
return { value: state.value - 1 };
default:
return state;
}
}
const [state, dispatch] = useReducer(reducer, { value: 0 });
return (
<div>
<p>ํ์ฌ ์นด์ดํฐ ๊ฐ์ {state.value}์
๋๋ค.</p>
<button onClick={() => dispatch({ type: "INCREMENT" })}>INCREMENT</button>
<button onClick={() => dispatch({ type: "DECREMENT" })}>DECREMENT</button>
</div>
);
};
export default App;
state,action๊ฐ์ ๊ฐ์ฒด๊ฐ ์๋ ๊ฐ์ผ๋ก ์ฌ์ฉํ์์ ๋
import React, { useReducer } from "react";
const Base = () => {
function reducer(state, action) {
switch (action) {
case "INCREMENT":
return state + 1;
case "DECREMENT":
return state - 1;
default:
return state;
}
}
const [state, dispatch] = useReducer(reducer, 0);
return (
<div>
<h1>value</h1>
<button onClick={() => dispatch("INCREMENT")}>INCREMENT</button>
<button onClick={() => dispatch("DECREMENT")}>DECREMENT</button>
</div>
);
};
export default Base;
๋ฆฌ๋์ค์์ ์ฌ์ฉํ๋ ์ก์ ๊ฐ์ฒด์๋ ์ด๋ค ์ก์ ์ธ์ง ์๋ ค ์ฃผ๋ type ํ๋๊ฐ ๊ผญ ์์ด์ผ ํ์ง๋ง, useReducer์์ ์ฌ์ฉํ๋ ์ก์ ๊ฐ์ฒด๋ ๋ฐ๋์ type์ ๊ฐ์ง๊ณ ์์ ํ์๊ฐ ์๋ค.
๊ทธ๋ฆฌ๊ณ useReducer์์๋ ์ก์ ๊ฐ์ด ๊ฐ์ฒด๊ฐ ์๋์ด๋ ๋๊ณ ๋ฌธ์์ด์ด๋ ์ซ์์ฌ๋ ์๊ด์๋ค.
if ๋ฌธ ๋ง๊ณ switch ๋ฌธ?
ํํ ์ฐ๋ if๋ฌธ์ ์ฐ์ง ์๊ณ ์ switch๋ฌธ์ ์ผ์๊น?
if (action === "INCREMENT") state + 1;
else if (action === "DECREMENT") state - 1;
else state;
์์ ๊ฐ์ด if๋ฌธ์ ์ผ์ ๋ ๊ฐ๋ ์ฑ์ด ์ข์ง ์์ ๊ฒ ๊ฐ๊ณ reducer์ ์ทจ์ง์ switch๋ฌธ์ด ์ ํฉํ๊ธฐ์ switch๋ฌธ์ ์ฌ์ฉํ๋ค๊ณ ์๊ฐ์ด ๋ค์๋ค.
switch ๋ฌธ์ ์ฌ๋ฌ ๊ฐ์ง case๋ฅผ ์ค์ ํ์ฌ ์กฐ๊ฑด์ ๋ง๋ case๋ฅผ ์ฌ์ฉํ ์ ์์ด useReducer์ ์ทจ์ง์ธ ์ฌ๋ฌ action ์ค ์ ํํด ์ฌ์ฉํ๋ค๋ ์๋ฏธ์์ ๋ ์๋ง์ ๊ฒ ๊ฐ๋ค.
import React, { useReducer } from "react";
const App = () => {
function reducer(state, action) {
return {
...state, //๊ธฐ์กด state๊ฐ ๊ฐ์ ธ์ค๊ธฐ
[action.name]: action.value,
};
}
const [state, dispatch] = useReducer(reducer, { name: "", nickname: "" });
const onChange = (e) => {
dispatch(e.target);
};
console.log("render");
return (
<div>
<div>
<input name="name" value={state.name} onChange={onChange} />
</div>
<div>
<input name="nickname" value={state.nickname} onChange={onChange} />
</div>
<h1>์ด๋ฆ : {state.name}</h1>
<h1>๋ณ์นญ : {state.nickname} </h1>
</div>
);
};
export default App;
reducer ํจ์์ switch๋ฌธ์ ์จ์ผ๋ง ํ๋ ๊ฒ๋ ์๋๊ณ action๊ฐ์ ๋ค์ํ ๊ฐ์ ํ ๋นํ ์๋ ์๋ค.
When use?
useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.
https://reactjs.org/docs/hooks-reference.html#usereducer
useReducer๋ ๋ณต์กํ state logic์ ๊ฐ์ง๊ณ ์์ ๋๋ ๋ค์ state๊ฐ ์ด์ state์ ์์กดํ๊ณ ์์ ๋ ์ฌ์ฉํ๋ค.
(action์ ๋ฐ๋ผ ์ฌ๋ฌ state๋ฅผ ์กฐ์ํ ์ ์๊ธฐ ๋๋ฌธ)