목표
- 커스텀 훅을 만들어 본다.
- 훅이 어떻게 발생하는지 흐름에 대해 알아본다.
const App = () => {
const [keyword, setKeyword] = React.useState(() => {
return window.localStorage.getItem("keyword") || "";
});
const [typing, setTyping] = React.useState(false);
const [result, setResult] = React.useState("");
React.useEffect(() => {
window.localStorage.setItem("keyword", keyword);
}, [keyword]);
// 생략
};
🤔 의문
- keyword state 변수 뿐만 아니라 typing, result state 변수 까지 localStorage에 저장하고 싶다면 어떻게 해야 할까?
const [keyword, setKeyword] = React.useState(() => {
return window.localStorage.getItem("keyword") || "";
});
const [typing, setTyping] = React.useState(false);
const [result, setResult] = React.useState("");
React.useEffect(() => {
window.localStorage.setItem("keyword", keyword);
}, [keyword]);
React.useEffect(() => {
window.localStorage.setItem("typing", typing);
}, [typing]);
React.useEffect(() => {
window.localStorage.setItem("result", result);
}, [result]);
// 생략
const useLocalStorage = (itemName, value = "") => {
const [state, setState] = React.useState(() => {
return window.localStorage.getItem(itemName) || value;
});
React.useEffect(() => {
window.localStorage.setItem(itemName, state);
}, [state]);
return [state, setState];
};
const App = () => {
const [keyword, setKeyword] = useLocalStorage("keyword");
const [typing, setTyping] = useLocalStorage("typing", false);
const [result, setResult] = useLocalStorage("result");
const onChange = (e) => {
setKeyword(e.target.value);
setTyping(true);
};
const onClick = () => {
setTyping(false);
setResult(`search result of ${keyword}`);
};
const onKeyPress = (e) => {
if (e.key === "Enter" && e.keyCode === 0) {
onClick();
}
};
// 생략
useState, useEffect 등과 같은 훅들이 언제 호출되고 언제 사라지는지, 그리고 컴포넌트가 여러 개일 때에 각 컴포넌트의 훅은 언제 호출 되는지 등에 대해서도 알아야 한다.
훅 플로우를 알기 위한 예제로 아래 코드는 버튼을 클릭하면 show state 변수를 이용해서 input 태그를 보여주거나 숨긴다. 그리고 p태그에 text를 계속 넣어준다.
const rootElement = document.getElementById("root");
const App = () => {
const [show, setShow] = React.useState(false);
const [text, setText] = React.useState("");
const onClick = () => {
setShow((prev) => !prev);
setText((prev) => prev + "test ");
};
return (
<>
<button onClick={onClick}>search</button>
{show ? (
<>
<input />
</>
) : null}
<p>{text}</p>
</>
);
};
ReactDOM.render(<App />, rootElement);
❗️ 참고
- 위 코드에서 onClick 함수를 보면 setShow와 setText의 인자로 prevState를 넘겨 주었다.
- 리액트에서는 useState로 만들어진 set함수들은 이전 값이 인자로 넘어오기 때문에 이 인자를 활용하여 코드를 더 간결하게 짤 수 있다.
- 아래 코드는 이전 값을 사용하지 않았을 때와 사용했을 때의 코드이다.
// 이전 값 사용 X const onClick = () => { if(show) { setState(false); } else { setState(true); } }; // 이전 값 사용 O const onClick = () => { setShow((prev) => !prev); };
const App = () => {
console.log("App Component Render Start");
const [show, setShow] = React.useState(() => {
console.log("App Component UseState");
return false;
});
const [text, setText] = React.useState("");
React.useEffect(() => {
console.log("App Component UseEffect No Defendency Array ");
});
React.useEffect(() => {
console.log("App Component UseEffect Empty Defendency Array");
}, []);
React.useEffect(() => {
console.log("App Component UseEffect Defendency Array");
}, [show]);
console.log("App Component Render End");
// 생략
};
ReactDOM.render(<App />, rootElement);
🤔 의문
- useEffect는 App 컴포넌트의 렌더링이 끝났을 때 발생한다는 것을 알 수 있다.
- 그렇다면 useEffect 간 순서는 어떤 흐름으로 동작하는 것일까?
useEffect를 선언한 순서대로 호출된다. 가령, 위 코드에서 useEffect 간 순서를 바꿔보면 다음과 같은 결과를 볼 수 있다.
const App = () => {
// 생략
React.useEffect(() => {
console.log("App Component UseEffect Defendency Array");
}, [show]);
React.useEffect(() => {
console.log("App Component UseEffect Empty Defendency Array");
}, []);
React.useEffect(() => {
console.log("App Component UseEffect No Defendency Array ");
});
// 생략
};
const Child = () => {
console.log("Child Component Render Start");
const [text, setText] = React.useState(() => {
console.log("Child Component useState");
});
const onChange = (e) => {
setText(e.target.value);
};
const element = (
<>
<input onChange={onChange} />
<p>{text}</p>
</>
);
console.log("Child Component Render End");
return element;
};
const App = () => {
console.log("App Component Render Start");
const [show, setShow] = React.useState(() => {
console.log("App Component UseState");
return false;
});
React.useEffect(() => {
console.log("App Component UseEffect Defendency Array");
}, [show]);
React.useEffect(() => {
console.log("App Component UseEffect Empty Defendency Array");
}, []);
React.useEffect(() => {
console.log("App Component UseEffect No Defendency Array ");
});
const onClick = () => {
setShow((prev) => !prev);
};
console.log("App Component Render End");
return (
<>
<button onClick={onClick}>search</button>
{show ? <Child /> : null}
</>
);
};
🤔 의문
- 그렇다면 Child 컴포넌트에서 useEffect는 언제 실행될까?
const Child = () => {
console.log("Child Component Render Start");
const [text, setText] = React.useState(() => {
console.log("Child Component useState");
});
// 생략
React.useEffect(() => {
console.log("Child Component UseEffect Defendency Array");
}, [text]);
React.useEffect(() => {
console.log("Child Component UseEffect Empty Defendency Array");
}, []);
React.useEffect(() => {
console.log("Child Component UseEffect No Defendency Array ");
});
// 생략
console.log("Child Component Render End");
return element;
};
const Child = () => {
// 생략
React.useEffect(() => {
console.log("Child Component UseEffect Defendency Array");
return () => {
console.log("Child Component Clean-Up Defendency Array");
};
}, [text]);
React.useEffect(() => {
console.log("Child Component UseEffect Empty Defendency Array");
return () => {
console.log("Child Component Clean-Up Empty Defendency Array");
};
}, []);
React.useEffect(() => {
console.log("Child Component UseEffect No Defendency Array ");
return () => {
console.log("Child Component Clean-Up No Defendency Array");
};
});
// 생략
};
const App = () => {
// 생략
React.useEffect(() => {
console.log("App Component UseEffect Defendency Array");
return () => {
console.log("App Component Clean-Up Defendency Array");
};
}, [show]);
React.useEffect(() => {
console.log("App Component UseEffect Empty Defendency Array");
return () => {
console.log("App Component Clean-Up Empty Defendency Array");
};
}, []);
React.useEffect(() => {
console.log("App Component UseEffect No Defendency Array ");
return () => {
console.log("App Component Clean-Up No Defendency Array");
};
});
// 생략
};