Today I Learned ... react.js
🙋♂️ *Reference Lecture
🙋 My Dev Blog
React Hooks 강의
-> 지난번에 만들었던 useClick에서 이벤트만 mouseenter로 바꿔준 것.
import { useEffect } from "react";
const useHover = (onHover) => {
const element = useRef();
useEffect(() => {
if (typeof onHover !== 'function') {
return;
}
if (element.current) {
element.current.addEventListener("mouseenter", onHover);
}
return () => {
if (element.current) {
element.current.removeEventListener("mouseenter", onHover)
}};
}, [onHover]);
return element;
};
export default useHover;
const useConfirm = (message, callback) => {
if (typeof callback !== "function") {
return;
}
const confirmAction = () => {
if (confirm(message)) { // 확인 누르면 콜백 실행
callback();
}
};
return confirmAction;
};
const App = () => {
const deleteWorld = () => console.log("delete world!");
const confirmDelete = useConfirm("Are you sure?", deleteWorld);
return (
<div>
<button onClick={confirmDelete}>delete the world</button>
</div>
);
};
confirm('message') 함수를 호출하여 확인을 누르면 true를 반환함.
-> if문의 조건을 충족하여 callback이 실행됨.
+) 취소를 눌렀을때 로직도 추가하면 다음과 같다.
const useConfirm = (message, onConfirm, onCancel) => {
if (!onConfirm && typeof onConfirm !== "function") {
return;
}
if (onCancel && typeof onCancel !== "function") {
return;
}
const confirmAction = () => {
if (window.confirm(message)) {
// 확인 누르면 콜백 실행
onConfirm();
} else {
onCancel();
}
};
return confirmAction;
};
-> useConfirm의 세번째 인자로 onCancel을 넣어줌.
-> 기존 callback은 구분을 위해 onConfirm으로 바꿔줌.
if (!onConfirm || typeof onConfirm !== "function") {
return;
}
if (onCancel && typeof onCancel !== "function") {
return;
}
onConfirm
은 반드시 있어야 하므로 !onConfirm이면 return되게 해야 하고,
또는 함수가 아니면 return되게 해야 한다.
onCancel
은 반드시 있어야 하는 것은 아니므로 그냥 onCancel이 함수인지만 검사하면 된다.
beforeunload
를 이용함.참고 - returnValue는 권장하지 X. (크롬에서만 지원) - mdn
const usePreventLeave = () => {
const listener = (e) => {
e.preventDefault();
e.returnValue = "";
};
const enablePrevent = () => {
window.addEventListener("beforeunload", listener);
};
const disablePrevent = () => {
window.removeEventListener("beforeunload", listener);
};
return { enablePrevent, disablePrevent };
};
const App = () => {
const { enablePrevent, disablePrevent } = usePreventLeave();
return (
<div>
<button onClick={enablePrevent}>protect</button>
<button onClick={disablePrevent}>unprotect</button>
</div>
);
};
protect
버튼을 클릭하고 창을 닫으면 아래와 같은 창이 뜬다.
useEffect
사용const useBeforeLeave = (onBefore) => {
if (typeof onBefore !== "function") {
return;
}
const handle = () => {
onBefore();
};
useEffect(() => {
document.addEventListener("mouseleave", handle);
return () => document.removeEventListener("mouseleave", handle);
}, []);
};
const App = () => {
const begForLife = () => console.log("plz dont leave");
useBeforeLeave(begForLife);
return (
<div>
<h1>hello</h1>
</div>
);
};
+) improveMent
->
mouseEvent를 이용하여 많은 정보를 알 수 있다.
clientY
를 보면 0보다 작은 경우에 (화면을 닫기위해 상단바로 마우스를 올리므로)참고 - clientX, offsetX, screenX의 차이
- clientX: 사용자의 브라우저 화면 기준
- offsetX: 이벤트 대상 DOM 객체 기준
- screenX: 모니터 화면 기준
const handle = (e) => {
const { clientY } = e;
if(clientY <= 0) onBefore();
};
event.clientY가 0보다 작거나 같을때,
(0일때도 포함됨) -> onBefore을 실행함.
이제 마우스가 창을 벗어날 때 (document.mouseleave), clientY가 0 이하일때만 onBefore 함수가 실행됨.
(그 외의 경우에는 전부 X)
const useBeforeLeave = (onBefore) => {
if (typeof onBefore !== "function") {
return;
}
const handle = (e) => {
const { clientY } = e;
if (clientY <= 0) onBefore();
};
useEffect(() => {
document.addEventListener("mouseleave", handle);
return () => document.removeEventListener("mouseleave", handle);
}, []);
};
const App = () => {
const begForLife = () => console.log("plz dont leave");
useBeforeLeave(begForLife);
return (
<div>
<h1>hello</h1>
</div>
);
};
const useFadeIn = (duration = 1, delay = 0) => {
const element = useRef();
if (typeof duration !== "number" || typeof delay !== "number") {
return;
}
useEffect(() => {
if (element.current) {
const { current } = element;
current.style.transition = `opacity ${duration}s ease-in ${delay}s`;
current.style.opacity = 1;
}
}, []);
return { ref: element, style: { opacity: 0 } };
};
const App = () => {
const fadeInH1 = useFadeIn(2, 2);
const fadeInP = useFadeIn(4, 6);
return (
<div>
<h1 {...fadeInH1}>hello</h1>
<p {...fadeInP}>lorem ipsum lalallala</p>
</div>
);
};
if (!['ease', 'ease-in', 'ease-out', 'ease-in-out'].includes(timing)) return;
const fadeInSpan = useFadeIn(3, 5, 'ease-in'); // 통과
const fadeInDiv = useFadeIn(3, 5, 'easy'); // 걸러짐. (효과X)
-> 기본값으로 ease를 주는게 좋을듯?
오타가 나더라도 ease가 적용되게 하려면
-> else에다가 timing = 'ease' 대입하면 됨.
online
과 offline
을 이용함.
navigator.onLine
이란?
- mdn 문서 참고.
- Returns the online status of the browser. The property returns a boolean value,
- with
true
meaning online andfalse
meaning offline.
const useNetwork = (onChange) => {
const [status, setStatus] = useState(navigator.onLine); // true 또는 false
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;
};
const App = () => {
const handleNetworkChange = (online) => {
console.log(online ? "online status" : "offline status");
};
const onLine = useNetwork(handleNetworkChange);
return (
<div>
<h1>{onLine ? "Online!" : "Offline"}</h1>
</div>
);
};