검증 기능이 있는 useInput
const useInput = (initialValue, validator) => {
const [value, setValue] = useState(initialValue);
const onChange = event => {
const { target: { value } } = event;
let willUpdate = true;
if(typeof validator === "function"){
willUpdate = validator(value);
}
if(willUpdate){
setValue(value);
}
}
return { value, onChange };
};
export default function App() {
// validator 함수
const maxLen = (value) => value.length <= 10;
const name = useInput("Mr.", maxLen);
return (
<div className="App">
<h1>Hello</h1>
<input placeholder="Name" {...name}/>
</div>
);
}
const content = [
{
tab: "Section 1",
content: "I'm the content of the Section 1"
},
{
tab: "Section 2",
content: "I'm the content of the Section 2"
}
]
const useTabs = (initialTab, allTabs) => {
const [currentIndex, setCurrentIndex] = useState(initialTab);
return {
currentItem: allTabs[currentIndex],
changeItem: setCurrentIndex
}
};
export default function App() {
const { currentItem, changeItem } = useTabs(0, content);
return (
<div className="App">
{content.map((section, index) => (
<button onClick={() => changeItem(index)}>{section.tab}</button>
))}
<div>
{ currentItem.content }
</div>
</div>
);
}
effect, dependency(배열:적용할 대상들) 인자로 전달
// componentDidMount + componentWillUpdate
useEffect(()=>{
sayHello()
})
// componentDidMount
useEffect(()=>{
sayHello()
},[])
// componentDidMount + componentWillUpdate
useEffect(()=>{
sayHello()
},[number])
문서 제목 업데이트
처음에 Loading... 이였다가 3초뒤 Home으로 변경
const useTitle = (initialTitle) => {
const [title, setTitle] = useState(initialTitle);
const updateTitle = () => {
const htmlTitle = document.querySelector("title");
htmlTitle.innerText = title;
};
useEffect(updateTitle, [title]);
return setTitle;
};
export default function App() {
const titleUpdater = useTitle("Loading...");
setTimeout(() => titleUpdater("Home"), 3000);
return (
<div className="App">
<h1>Hi</h1>
</div>
);
}
클릭시 함수 실행(= onClick)
const useClick = (onClick) => {
const element = useRef(); // HTML 태그 연결
useEffect(() => {
if (element.current) { // 반드시 current로 접근
element.current.addEventListener("click", onClick);
}
// 반환된 함수는 componentWillUnmount일때 동작(이벤트 리스너 삭제)
return () => {
if (element.current) {
element.current.removeEventListener("click", onClick);
}
};
}, []); // 한번만 실행
return element;
};
export default function App() {
const sayHello = () => console.log("say hello");
const title = useClick(sayHello);
return (
<div className="App">
<h1 ref={title}>Hi</h1>
</div>
);
}
const useHover = (onHover) => {
const element = useRef();
useEffect(() => {
if (element.current) {
element.current.addEventListener("mouseenter", onHover);
}
return () => {
if (element.current) {
element.current.removeEventListener("mouseenter", onHover);
}
};
}, []);
return element;
};
export default function App() {
const sayHello = () => console.log("say hello");
const title = useHover(sayHello);
return (
<div className="App">
<h1 ref={title}>Hi</h1>
</div>
);
}
사용자가 무언가를 하기전에 확인
const useConfirm = (message = "", onConfirm, onCancel) => {
const confirmAction = () => {
if (confirm(message)) {
onConfirm();
} else {
onCancel();
}
};
return confirmAction;
};
window 창 닫기 전 알림
const usePreventLeave = () => {
const listener = (event) => {
event.preventDefault();
event.returnValue = ""; // 이걸 넣어야 동작함
};
const enablePreve = () => window.addEventListener("beforeunload", listener);
const disablePreve = () =>
window.removeEventListener("beforeunload", listener);
return { enablePreve, disablePreve };
};
마우스가 창을 벗어날 때 함수 실행
const useBeforeLeave = (onBefore) => {
useEffect(() => {
document.addEventListener("mouseleave", onBefore);
return () => document.removeEventListener("mouseleave", onBefore);
}, []);
};
const useFadeIn = (duration = 1, delay = 0) => {
const element = useRef();
useEffect(() => {
if (element.current) {
const { current } = element;
current.style.transition = `opacity ${duration}s ease-in-out ${delay}s`;
// delay 시간 뒤에 duration 시간에 걸쳐서 화면에 렌더링
current.style.opacity = 1;
}
}, []);
return { ref: element, style: { opacity: 0 } };
};
export default function App() {
const fadeInH1 = useFadeIn(1, 2);
const fadeInP = useFadeIn(5, 10);
return (
<div className="App">
<h1 {...fadeInH1}>Hello</h1>
<p {...fadeInP}>lalalalala</p>
</div>
);
}
navigator의 online 또는 offline 감지
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;
};
export default function App() {
const handleNetworkChange = (online) => {
console.log(online ? "We just went online" : "We are offline");
};
const onLine = useNetwork(handleNetworkChange);
return (
<div className="App">
<h1>{onLine ? "Online" : "Offline"}</h1>
</div>
);
}
const useScroll = () => {
const [state, setState] = useState({ x: 0, y: 0 });
const onScroll = () => {
setState({ y: window.scrollY, x: window.scrollX });
};
useEffect(() => {
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
return state;
};
export default function App() {
const { y } = useScroll();
return (
<div className="App" style={{ height: "1000vh" }}>
<h1 style={{ position: "fixed", color: y > 100 ? "red" : "blue" }}>Hi</h1>
</div>
);
}
이미지 확대하기
const useFullscreen = (callback) => {
const element = useRef();
const triggerFull = () => {
if (element.current) {
if (element.current.requestFullscreen) { // 크롬
element.current.requestFullscreen();
} else if (element.current.mozRequestFullScreen) { /// firefox
element.current.mozRequestFullScreen();
} else if (element.current.webkitRequestFullscreen) { // opera
element.current.webkitRequestFullscreen();
} else if (element.current.msRequestFullscreen) { // Microsoft
element.current.msRequestFullscreen();
}
callback(true);
}
};
const exitFull = () => {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
callback(false);
};
return { element, triggerFull, exitFull };
};
export default function App() {
const onFullS = (isFull) => {
console.log(isFull ? "We are full" : "We are small");
};
const { element, triggerFull, exitFull } = useFullscreen(onFullS);
return (
<div className="App" style={{ height: "1000vh" }}>
<div ref={element}>
<img src="https://placeimg.com/256/198/any" />
<button onClick={exitFull}>Exit fullscreen</button>
</div>
<button onClick={triggerFull}>Make fullscreen</button>
</div>
);
}
크롬 우측 상단에 알림 띄우기(notification 허용 필요)
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;
};
export default function App() {
const triggerNotif = useNotification("my Notification", {
body: "I love kimchi"
});
return (
<div className="App">
<button onClick={triggerNotif}>Hello</button>
</div>
);
}
refetch 가능한 데이터 fetch
import { useEffect, useState } from "react";
import axios from "axios";
const useAxios = (opts, axiosInstance = axios) => {
const [state, setState] = useState({
loading: true,
error: null,
data: null
});
const [trigger, setTrigger] = useState(0);
const refetch = () => {
setState({
...state,
loading: true
});
setTrigger(Date.now());
};
useEffect(() => {
axiosInstance(opts)
.then((data) => {
setState({
...state,
loading: false,
data
});
})
.catch((error) => {
setState({ ...state, loading: false, error });
});
}, [trigger]);
return { ...state, refetch };
};
export default function App() {
const { loading, error, data, refetch } = useAxios({
url: "https://yts.am/api/v2/list_movies.jsoin" // 현재는 안되는 url
});
return (
<div className="App">
<h1>{data && data.status}</h1>
<h2>{loading && "Loading"}</h2>
<button onClick={refetch}>Refetch</button>
</div>
);
}
깔끔한 정리 감사합니다!