커스텀 hook 만들기
유효성을 검증하는 기능이 있는 useInput 이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| import React, { useState } from "react";
const useInput = (initialValue, validator) => {
const [value, setValue] = useState(initialValue);
const onChange = (event) => {
const {
target: { value },
} = event;
//event.target = {value}
let willUpdate = true;
if (typeof validator === "function") {
willUpdate = validator(value);
}
//validator에 함수가 들어오면 willUpdate에 해당 함수를 넣는다.
if (willUpdate) {
setValue(value);
}
//willUpdate가 true일 때 setValue는 value를 가리킨다.
};
return { value, onChange };
};
function App() {
const maxLen = (value) => value.length < 10;
//유효성 검사에 value의 길이가 10미만이라는 조건을 넣는다.
const name = useInput("Mr.", maxLen);
//useInput을 사용하는 방법
return (
<div>
<h1>hello</h1>
<input placeholder="name" {...name} />
//{...name}은 value={name.value} onChange={name.onChange} 를 모두
포함한다.
</div>
);
}
export default App;
|
useTabs
선택한 tab의 content를 보여주는 hooks
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| import React, { useState } from "react";
const content = [
{
tab: "Section 1",
content: "Content 1 from Section 1",
},
{
tab: "Section 2",
content: "Content 2 from Section 2",
},
];
const useTabs = (initialTab, allTabs) => {
if (!allTabs || !Array.isArray(allTabs)) {
return;
}
// tab이 없거나 tab이 배열이 아닐 때 아무 일도 일어나지 않는다.
const [currentIndex, setCurrentIndex] = useState(initialTab);
return {
currentItem: allTabs[currentIndex],
changeItem: setCurrentIndex,
};
//최근의 index가 currentItem으로, 새로 바뀔 아이템의 index가 setCurrentIndex로 간다.
};
function App() {
const { currentItem, changeItem } = useTabs(0, content);
//useTabs의 배열 0번을 초기값으로, content배열을 allTabs로 넣어준다.
//{currentItme: content[0], changeItem: changeItem}
return (
<div className="App">
{content.map(
(section, index) => (
<button onClick={() => changeItem(index)}>{section.tab}</button>
)
//각 tab에 map의 index를 넣어주고 click시 해당 tab의 index가 changeItem에 들어가서 setcurrentIndex값이 된다.
)}
<div>{currentItem.content}</div>
//setCurrentIndex를 통해 currentIndex가 바뀌어서 currentItem의 내용도
바뀐다.
</div>
);
}
export default App;
|
useTitle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| import React, { useState, useEffect } from "react";
const useTitle = (initialTitle) => {
const [title, setTitle] = useState(initialTitle);
const updateTitle = () => {
const htmlTitle = document.querySelector("title");
htmlTitle.innerText = title;
//updateTitle은 html의 title element를 선택해서 현재의 title로 바꾸어주는 역할을 한다.
};
useEffect(updateTitle, [title]);
//title이 바뀌면 title의 innerText를 바꾸는 updateTitle이 실행된다.
return setTitle;
};
function App() {
const titleUpdater = useTitle("loading...");
setTimeout(() => titleUpdater("Home"), 5000);
//5초 뒤에 titleUpdater, 즉 useTitle에 인자 값을 넣어 실행한다.
return <div className="App"></div>;
}
export default App;
|
useClick
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| import React, { useEffect, useRef } from "react";
const useClick = (onClick) => {
const element = useRef();
useEffect(() => {
if (element.current) {
element.current.addEventListener("click", onClick);
}
//componentDidMount시에 일어나는 이벤트
return () => {
if (element.current) {
element.current.removeEventLister("cilck", onClick);
}
//componentWillUnMount시에 일어나는 이벤트 : component가 mount되지 않았을 때 eventListner를 배치하지 않기 위해
};
}, []);
//[]를 넣어서 componentDidMount시에만 렌더링되라는 의미가 된다.
return element;
};
function App() {
const sayHello = () => console.log("say hello");
const title = useClick(sayHello);
return (
<div className="App">
<h1 ref={title}>title</h1>
</div>
);
}
export default App;
|
useConfirm
삭제버튼 클릭 -> 정말로 삭제하시겠습니까? -> 예 : deleting / 아니오 : aborted
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| import React from "react";
const useConfirm = (message = "", onConfirm, onCancel) => {
//alert창 내용, 확인시 실행할 callback, 취소시 실행할 reject
if (!onConfirm && typeof onConfirm !== "function") {
return;
}
if (onCancel && typeof onCancel !== "function") {
return;
}
//onCancel은 필수적인 것이 아니라서 !안붙임
const confirmAction = () => {
if (confirm(message)) {
onConfirm();
//message를 확인시 실행
} else {
onCancel();
//message를 취소시 실행
}
};
return confirmAction;
};
function App() {
const deletePost = () => console.log("Deleting post...");
const abort = () => console.log("Aborted");
const confirmDelete = useConfirm("Are you sure", deletePost, abort);
return (
<div className="App">
<button onClick={confirmDelete}>delete</button>
</div>
);
}
export default App;
|
- window confirm
esult = window.confirm(message);
alert처럼 뜨는 경고 대화 상자에 message를 넣을 수 있다.
usePreventLeave
페이지에서 무언가 작성하고 있을 때, 창 닫기를 누르면 나가시겠습니까? 변경된 내용이 저장되지 않을 수도 있습니다.
와 같은 메세지가 뜨는 것
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| import React from "react";
const usePreventLeave = () => {
const listener = (event) => {
event.preventDefault();
event.returnValue = "";
//이 부분을 써주지 않으면 크롬에서 작동하지 않는다. 별 의미는 없다.
};
const enablePrevent = () => window.addEventListener("beforeunload", listener);
//beforeunload는 창을 닫기 전을 말한다.
const disablePrevent = () =>
window.removeEventListener("beforeunload", listener);
return { enablePrevent, disablePrevent };
};
function App() {
const { enablePrevent, disablePrevent } = usePreventLeave();
return (
<div className="App">
<button onClick={enablePrevent}>Protect</button>
<button onClick={disablePrevent}>unprotect</button>
</div>
);
}
export default App;
|
useBeforeLeave
window에서 마우스가 떠날 때 호출된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| import React, { useEffect } from "react";
const useBeforeLeave = (onBefore) => {
if (typeof onBefore !== "function") {
return;
}
const handle = (event) => {
const { clientY } = event;
if(clientY =< 0) {
onBefore();
}
// clientY는 console.log(event)를 해보면 나오는 mouse가 가진 객체 중 하난데, 마우스가 화면을 떠나기 전 마지막 y축의 좌표값을 반환한다. clientY가 0보다 작거나 같다는 것은 마우스가 화면을 떠났을 때를 의미한다.
};
useEffect(() => {
document.addEventListener("mouseleave", handle);
return () => document.removeEventListener("mouseleave", handle);
}, []);
};
function App() {
const begNotToLeave = () => console.log("plz don't leave");
useBeforeLeave(begNotToLeave);
//document에 주는 이벤트라서 element에 넣지 않아도 된다.
return (
<div className="App">
<h1>hello</h1>
</div>
);
}
export default App;
|
useFadeIn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| import React, { useEffect, useRef } from "react";
const useFadeIn = (duration = 1, delay = 0) => {
if (typeof duration !== "number" || typeof delay !== "number") {
return;
}
const element = useRef();
useEffect(() => {
if (element.current) {
const { current } = element;
current.style.trasition = `opacity ${duration}s ease-in-out ${delay}s`;
current.style.opacity = 1;
}
}, []);
return { ref: element, style: { opacity: 0 } };
};
function App() {
const fadeInH1 = useFadeIn(1, 2);
const fadeInP = useFadeIn(5, 10);
return (
<div className="App">
<h1 {...fadeInH1}>hello</h1>
<p {...fadeInP}>how are you?</p>
</div>
);
}
export default App;
|
useNetwork
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| import React, { useEffect, useState } from "react";
const useNetwork = (onChange) => {
const [status, setStatus] = useState(navigator.onLine);
const handleChange = () => {
if (typeof onchange === "function") {
onchange(navigator);
}
setStatus(navigator.onLine);
};
useEffect(() => {
window.addEventListener("online", handleChange);
window.addEventListener("offline", handleChange);
() => {
window.removeEventListener("online", handleChange);
window.removeEventListener("offline", handleChange);
};
}, []);
return status;
};
function App() {
const handleNetworkChange = (online) => {
console.log(online ? "we just went online" : "we are online");
};
const online = useNetwork(handleNetworkChange);
return (
<div className="App">
<h1>{online ? "online" : "offline"}</h1>
</div>
);
}
export default App;
|
아래로 스크롤하면 h1의 색상이 빨강으로, 위로 스크롤하면 파랑으로 변함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| import React, { useEffect, useState } from "react";
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;
};
function App() {
const { y } = useScroll();
return (
<div className="App" style=>
<h1 style=>
tian
</h1>
</div>
);
}
export default App;
|
useFullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| import React, { useRef } from "react";
const useFullscreen = (callback) => {
const element = useRef();
const triggerFullscreen = () => {
if (element.current) {
element.current.requestFullscreen();
if (callback && typeof callback === "function") {
callback(true);
}
}
};
const exitFullscreen = () => {
document.exitFullscreen();
if (callback && typeof callback === "function") {
callback(false);
}
};
return { element, triggerFullscreen, exitFullscreen };
};
function App() {
const onFullscreen = (isFull) => {
console.log(isFull ? "we are full" : "we are small");
};
const { element, triggerFullscreen, exitFullscreen } =
useFullscreen(onFullscreen);
return (
<div className="App" style=>
<div ref={element}>
<img
src="https://images.unsplash.com/photo-1612875895771-76bba1a61a49?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80"
alt="image"
/>
<button onClick={triggerFullscreen}>make fullscreen </button>
<button onClick={exitFullscreen}>exit fullscreen </button>
</div>
</div>
);
}
export default App;
|
useNotification
위치정보 같은 것을 수집할 때 브라우저에 등장하는 허용/차단 알림창
Notification web api
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| import React from "react";
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("may I help you?", "description");
return (
<div className="App" style=>
<button onClick={triggerNotif}>hello</button>
</div>
);
}
export default App;
|
useAxios
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| import React from "react";
import useAxios from "./hooks/useAxios";
function App() {
const { loading, data, error, refetch } = useAxios({
url: `${url}`,
});
console.log(
`loading: ${loading}\n data:${JSON.stringify(data)} \n error: ${error}`
);
return (
<div className="App" style=>
<h1>{data && data.status}</h1>
<h2>{loading && "Loading"}</h2>
<button onClick={refetch}>refetch</button>
</div>
);
}
export default App;
|
useAxios.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| import defaultAxios from "axios";
import { useEffect, useState } from "react";
const useAxios = (options, axiosInstance = defaultAxios) => {
const [state, setState] = useState({
loading: true,
error: null,
data: null,
});
const [trigger, setTrigger] = useState(0);
if (!options.url) {
return;
}
const refetch = () => {
setState({
...state,
loading: true,
});
setTrigger(Date.now());
};
useEffect(() => {
axiosInstance(options)
.then((data) => {
setState({
...state,
loading: false,
data,
});
})
.catch((error) => {
setState({ ...state, loading: false, error });
});
}, [trigger]);
return { ...state, refetch };
};
export default useAxios;
|