Hook : It allows you to have “state” in “functional components” → functional programming!
** Before you start, you need to import below first.
import React, { useEffect, useState, useRef } from "react";
import ReactDOM from "react-dom";
** Tip! Below two have the same function.
function APP() {}
const APP = () => {};
- useState: first factor is value ((ex)item) and the other is modifying value((ex) setItem). And it returns Array.
const App = () => {
const [item, setItem] = useState(1);
const incrementItem = () => setItem(item + 1);
const decrementItem = () => setItem(item - 1);
return (
<div className="App">
<h1>Hello {item}</h1>
<button onClick={incrementItem}>Increment</button>
<button onClick={decrementItem}>Decrement</button>
</div>
);
};
1–1. 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 };
};const App = () => {
const maxLen = (value) => value.length <= 10;
const name = useInput("Mr. ", maxLen);
return (
<div className="App">
<h1>Hello</h1>
<input placeholder="Name" {...name}/>
</div>
);
};
1–2. useTabs
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) => {
if (!allTabs || !Array.isArray(allTabs)) {
return;
}
const [currentIndex, setCurrentIndex] = useState(initialTab);
return {
currentItem: allTabs[currentIndex],
changeItem: setCurrentIndex
};
};const 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>
);
};
- useEffect:
- componentDidMount + componentWillUnMount + componentDidUpdate, if you do
useEffect(sayHello); That will watch every changes. - componentDidMount only and no update, if you do
useEffect(sayHello, []); - componentDidUpdate only, if you do
useEffect(sayHello, [number]); That will call sayHello function only when number is updated. dependency([ ]) is very important!
const App = () => {
const sayHello = () => console.log("hello");
const [number, setNumber] = useState(0);
const [aNumber, setAnumber] = useState(0);
useEffect(sayHello, [number]);
return (
<div className="App">
<div>Hi</div>
<button onClick={() => setNumber(number + 1)}>{number}</button>
<button onClick={() => setAnumber(aNumber + 1)}>{aNumber}</button>
</div>
);
};
2–1. useTitle : to update your document title
const useTitle = (initialTitle) => {
const [title, setTitle] = useState(initialTitle);
const updateTitle = () => {
const htmlTitle = document.querySelector("title");
htmlTitle.innerText = title;
};
useEffect(updateTitle, [title]);
return setTitle;
};const App = () => {
const titleUpdater = useTitle("Loading...");
setTimeout(() => titleUpdater("Home"), 3000);
return (
<div className="App">
<div>Hi</div>
</div>
);
};
2–2. useClick
When component mount, we add EventListener (it’s gonna be forever as we didn’t add any dependency([])), and it returns a function that will be working as componentWillUnMount which is removing the EventListener.
const useClick = (onClick) => {
if (typeof onClick !== "function") {
return;
}
const element = useRef();
useEffect(() => {
if (element.current) {
element.current.addEventListener("click", onClick);
}
return () => {
if (element.current) {
element.current.removeEventListener("click", onClick);
}
};
}, []);
return element;
};const App = () => {
const sayHello = () => console.log("say hello");
const title = useClick(sayHello);
return (
<div className="App">
<h1 ref={title}>Hi</h1>
</div>
);
};
2–3. useHover
const useHover = onHover => {
if (typeof onHover !== "function") {
return;
}
const element = useRef();
useEffect(() => {
if (element.current) {
element.current.addEventListener("mouseenter", onHover);
}
return () => {
if (element.current) {
element.current.removeEventListener("mouseenter", onHover);
}
};
}, []);
return element;
};
2–4. useConfirm
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;
};const App = () => {
const deleteWorld = () => console.log("Deleting the world...");
const abort = () => console.log("Aborted");
const confirmDelete = useConfirm("Are you sure", deleteWorld, abort);
return (
<div className="App">
<button onClick={confirmDelete}>Delete the world</button>
</div>
);
};
2–5. usePreventLeave : when users try to leave the page, it pops up the warning message saying “changes you made may not be saved”.
const usePreventLeave = () => {
const listener = (event) => {
event.preventDefault();
event.returnValue = "";
};
const enablePrevent = () => window.addEventListener("beforeunload", listener);
const disablePrevent = () =>
window.removeEventListener("beforeunload", listener);
return { enablePrevent, disablePrevent };
};const App = () => {
const { enablePrevent, disablePrevent } = usePreventLeave();
return (
<div className="App">
<button onClick={enablePrevent}>Protect</button>
<button onClick={disablePrevent}>Unprotect</button>
</div>
);
};
2–6. useBeforeLeave
const useBeforeLeave = (onBefore) => {
if (typeof onBefore !== "function") {
return;
}
const handle = (event) => {
const { clientY } = event;
if (clientY <= 0) {
onBefore();
}
};
useEffect(() => {
document.addEventListener("mouseleave", handle);
return () => document.removeEventListener("mouseleave", handle);
}, []);
};const App = () => {
const begForLife = () => console.log("pls dont leave");
useBeforeLeave(begForLife);
return (
<div className="App">
<h1>Hello</h1>
</div>
);
};
2–7. useFadeIn
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.transition = `opacity ${duration}s ease-in-out ${delay}s`;
current.style.opacity = 1;
}
}, []);
return { ref: element, style: { opacity: 0 } };
};const App = () => {
const fadeInH1 = useFadeIn(1, 2);
const fadeInp = useFadeIn(5, 10);
return (
<div className="App">
<h1 {...fadeInH1}>Hello</h1>
<p {...fadeInp}>lalalalala</p>
</div>
);
};
2–8. useNetwork: It’s going to detect when navigator goes online or 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);
() => {
window.removeEventListener("online", handleChange);
window.removeEventListener("offline", handleChange);
};
}, []);
return status;
};const 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>
);
};
2–9. useScroll
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;
};const App = () => {
const { y } = useScroll();
return (
<div className="App" style={{ height: "1000vh" }}>
<h1 style={{ position: "fixed", color: y > 100 ? "red" : "blue" }}>Hi</h1>
</div>
);
};
2–10. useFullscreen
const useFullscreen = (callback) => {
const element = useRef();
const runCb = (isFull) => {
if (callback && typeof callback === "function") {
callback(isFull);
}
};
const triggerFull = () => {
if (element.current) {
if (element.current.requestFullscreen) {
element.current.requestFullscreen();
} else if (element.current.mozRequestFullScreen) {
element.current.mozRequestFullScreen();
} else if (element.current.webkitRequestFullscreen) {
element.current.webkitRequestFullscreen();
} else if (element.current.msRequestFullscreen) {
element.current.msRequestFullscreen();
}
runCb(true);
}
};
const exitFull = () => {
document.exitFullscreen();
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullscreen) {
document.mozCancelFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
runCb(false);
};
return { element, triggerFull, exitFull };
};const 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="..." />
<button onClick={exitFull}>Exit fullscreen</button>
</div>
<button onClick={triggerFull}>Make fullscreen</button>
</div>
);
};
2–10. useNotification : please refer to ‘MDN 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;
};const App = () => {
const triggerNotif = useNotification("can I steal your kimchi?", {
body: "I love kimchi dont you"
});
return (
<div className="App" style={{ height: "1000vh" }}>
<button onClick={triggerNotif}>Hello</button>
</div>
);
};
2–11. useAxios :
import defaultAxios from "axios";const useAxios = (opts, axiosInstance = defaultAxios) => {
const [state, setState] = useState({
loading: true,
error: null,
data: null
});
const [trigger, setTrigger] = useState(0);
if (!opts.url) {
return;
}
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 };
};const App = () => {
const { loading, data, refetch } = useAxios({
url: "https://yts.mx/api/v2/list_movies.json"
});
return (
<div className="App" style={{ height: "1000vh" }}>
<h1>{data && data.status}</h1>
<h2>{loading && "Loading"}</h2>
<button onClick={refetch}>Refetch</button>
</div>
);
};
What to learn next to deep into hooks…?
- useContext
- useReducer
- useCallback
- useMemo