리액트 훅이란
클래스 기반 컴포넌트의 장점(내부 state, 생명주기 메소드 etc)을 함수형 컴포넌트로 가져오려는 리액트의 시도
useState
useState는 항상 2개의 value를 return
// 이런 식으로 사용
const [item, setItem] = useState(1);
// item만 쓰고 싶다면
const [item] = useState(1)[0];
useInput
아래와 같은 방식으로 hook처럼 동작하게 할 수 있다.
validator를 넣음으로서 조건에 맞는 경우에만 name을 업데이트함
const useInput = (initialValue, validator) => {
const [value, setValue] = useState(initialValue);
const onChange = (e) => {
const {
target: {value}
} = e;
let willUpdate = true;
if (typeof validator === 'function') {
willUpdate = validator(value);
}
if (willUpdate) {
setValue(value);
}
}
return {value, onChange};
}
export default function App() {
const maxLen = value => value.length <= 10;
const name = useInput("Mr. ", maxLen);
return (
<div className="App">
<h1>Hello</h1>
<input placeholder="name" {...name}></input>
</div>
);
}
useEffect
useEffect
는 componentDidMount
,componentWillUnmount
, componentDidUpdate
의 역할을 수행한다.
useEffect
는 2개의 인자를 받는다.
첫번째는 function으로서의 effect
두번째는 deps : 만약 deps가 있다면 effect는 리스트에 있는 값일 때만 값이 변하도록 활성화
useEffect(sayHello, [number]);
/*
number가 변할 때에만 sayHello가 실행된다.
*/
useTitle
문서 제목 업데이트해주는 hook
보통 helmet을 쓴다
const useTitle = (initialTitle) => {
const [title, setTitle] = useState(initialTitle);
const updateTitle = () => {
const htmlTitle = document.querySelector("title");
htmlTitle.innerText = title;
};
useEffect(updateTitle, [title]);
return setTitle;
};
~~
// App 내에서 이런 식으로 사용 가능
const titleUpdater = useTitle("Loading...");
setTimeout(() => titleUpdater("home"), 3000);
useRef
reference는 기본적으로 우리의 Component의 어떤 부분을 선택할 수 있는 방법
document.getElementByID()
와 동등함
react의 모든 Component는 reference element를 가진다
export default function App() {
const potato = useRef();
// setTimeout(() => potato.current?.focus(), 3000)
useEffect(() => {
setTimeout(() => potato.current.focus(), 3000)
})
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<input ref={potato} placeholder="test"></input> // reference 갖게 한다
</div>
);
}
// useClick 사용해서 useRef() 만들고
const useClick = (onClick) => {
const element = useRef(); // 얘가 제일 중요
useEffect(() => {
if (element.current) {
element.current.addEventListener('click', onClick);
}
// 아래 function은 componentWillUnmount 때 호출
return () => {
if (element.current) {
element.current.removeEventListener('click', onClick);
}
}
}, []) // 이 dependency가 없으면 매번 update될 때마다 eventListener가 추가된다
// componentDidMount때 단 한번만 실행
return element; // 같은 reference를 return
}
export default function App() {
const sayHello = () => console.log('say hello');
const title = useClick(sayHello);
return (
<div className="App">
<h1 ref={title}>Hello CodeSandbox</h1>
</div>
);
}
componentWillUnmount
될 때 eventListener
를 지워야 한다.
function을 리턴받았다면 그 function은 componentWillUnmount
로부터 호출된 것
component가 mount 되지 않았을 때 eventListener가 배치되게 하고 싶지 않다
useConfirm
사용자가 클릭했을 때 confirm
// 2. 얘가 실행되고
const useConfirm = (message = "", onConfirm, onCancel) => {
if (!onConfirm || typeof onConfirm !== "function") {
return;
}
if (onCancel && typeof onCancel !== "function") {
return;
}
const confirmAction = () => {
if (window.confirm(message)) { // 3. 우리가 true라고 하면
onConfirm(); // 4. 콜백 실행
} else {
onCancel();
}
}
return confirmAction;
}
function App() {
const deleteWorld = () => console.log('Deleting the world!');
const abort = () => console.log("Aborted");
const confirmDelete = useConfirm("Are you sure", deleteWorld, abort);
return (
<div className="hello">
<h1>Hello React Hook</h1>
<button onClick={confirmDelete}>Delete the world</button> // 1. 버튼 클릭하면
</div>
);
}
usePreventLeave
사용자가 페이지 떠나는 걸 방지
const usePreventLeave = () => {
const listener = (event) => {
event.preventDefault();
event.returnValue = "";
}
const enablePrevent = () => window.addEventListener("beforeunload", listener);
const disablePrevent = () => window.removeEventListener("beforeunload", listener);
return {enablePrevent, disablePrevent};
}
function App() {
const {enablePrevent, disablePrevent} = usePreventLeave();
return (
<div className="hello">
<h1>Hello React Hook</h1>
<button onClick={enablePrevent}>Protect</button>
<button onClick={disablePrevent}>Unprotect</button>
</div>
);
}
beforeunload
는 window가 닫히기 전에 funciton이 실행되는 걸 허락한다.
beforeunload
이벤트를 사용하면 사용자가 페이지를 떠날 때 정말로 떠날 것인지 묻는 확인 대화 상자를 표시할 수 있습니다. 사용자가 확인을 누를 경우 브라우저는 새로운 페이지로 탐색하고, 취소할 경우 탐색을 취소하고 현재 페이지에 머무릅니다.
useBeforeLeave
사용자 마우스가 떠나는 걸 방지
const useBeforeLeave = (onBefore) => {
const handle = (event) => {
const {clientY} = event;
if (clientY <= 0) { // 마우스가 현재 창 떠날 때
onBefore();
}
};
useEffect(() => {
if (typeof onBefore === "function") {
document.addEventListener("mouseleave", handle);
return () => document.removeEventListener("mouseleave", handle);
} else {
return;
}
}, []);
}
~~~
const begForLife = () => console.log("Plz don't leave");
useBeforeLeave(begForLife);
useFadeIn
나타나는 효과 주기
const useFadeIn = (duration = 1, delay = 0) => {
const element = useRef();
useEffect(() => {
if (typeof duration === "number" && typeof delay === "number") {
if(element.current) {
const {current} = element;
current.style.transition = `opacity ${duration}s ease-in-out ${delay}s`;
current.style.opacity = 1;
}
} else {
return;
}
}, [])
return {ref: element, style: {opacity:0}};
}
function App() {
const fadeInH1 = useFadeIn(2, 2);
const fadeInP = useFadeIn(5, 10);
return (
<div className="hello">
<h1 {...fadeInH1}>Hello</h1>
<p {...fadeInP}>lorem ipsum lalalla</p>
</div>
);
}
useNetwork
인터넷 연결 확인하기
const useNetwork = (onChange) => {
const [status, setStatus] = useState(navigator.onLine);
const handleChange = () => {
if (typeof onChange === "function") {
onChange(navigator.onLine);
}
setStatus(navigator.onLine);
}
useEffect(() => {
window.addEventListener("online", handleChange);
window.addEventListener("offline", handleChange);
return () => {
window.removeEventListener("online", handleChange);
window.removeEventListener("offline", handleChange);
}
}, []);
return status;
}
function App() {
const handleNetworkChange = (online) => console.log(online ? "We just went onoine" : "We are offline");
const onLine = useNetwork(handleNetworkChange);
return (
<div className="hello">
<h1>{onLine ? "Online" : "OffLine"}</h1>
</div>
);
}
useScroll
사용자의 스크롤 동작 감지
const useScroll = () => {
const [state, setState] = useState({
x: 0,
y: 0
});
const onScroll = () => {
console.log("y : ", window.scrollY, " x : ", window.scrollX);
setState({y:window.scrollY, x:window.scrollX});
}
useEffect(() => {
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
})
return state;
}
function App() {
const {y} = useScroll();
return (
<div className="hello" style={{height:"1000vh"}}>
<h1 style={{position:"fixed", color: y > 100 ? "red" : "blue"}}>HELLO</h1>
</div>
);
}
useFullscreen
전체 화면 만들고 사용자에게 알려주기
const useFullscreen = (callback) => {
const element = useRef();
const triggerFull = () => {
if (element.current) {
element.current.requestFullscreen();
if(callback && typeof callback === "function") {
callback(true);
}
}
}
const exitFull = () => {
document.exitFullscreen();
if(callback && typeof callback === "function") {
callback(false);
}
}
return {element, triggerFull, exitFull};
}
function App() {
const onFulls = (isFull) => {
console.log(isFull ? "We are full" : "We are small");
}
const {element, triggerFull, exitFull} = useFullscreen(onFulls);
return (
<div className="hello">
Hello
<div ref={element}>
<img
src="이미지 경로"
/>
<button onClick={exitFull}>Exit fullscreen</button>
</div>
<button onClick={triggerFull}>Make fullscreen</button>
</div>
);
}
useNotification
알람
const useNotification = (title, options) => {
if(!("Notification" in window)) {
return;
}
const fireNotif = () => {
if(Notification.permission !== "granted") {
Notification.requestPermission().then(permission => {
if (permission === "granted") {
new Notification(title, options);
} else {
return;
}
})
} else {
new Notification(title, options);
}
}
return fireNotif;
}
function App() {
const triggerNotif = useNotification("알람 제목", {
body: "알람 메시지"
});
return (
<div className="hello">
<button onClick={triggerNotif}>Hello</button>
</div>
);
}