[React Hook 시리즈 3]: 10가지 Hook들 (useMemo, useRef 등)

1. useMemo()
useMemo(() => value, deps), useMemo(fn, deps)
const memoizedValue = useMemo(()=>computedExpensiveValue(a,b),[a,b])
-
랜더링 될 때마다 값을 계속 계산하지 않고, 이전에 계산된 값을 메모이제이션하여 불필요한 연산을 줄인다.
-
새롭게 계산하는 기준은 두번째 인자, dependency에 적는다.
import { useMemo, useState } from 'react';
const App = () => {
console.log('리랜더링');
const [isOn, setIsOn] = useState(false);
const [users, setUsers] = useState([
{ active: true },
{ active: true },
{ active: false },
]);
const countActiveUser = () => {
console.log('사용자 수 세는중...');
return users.filter((user) => user.active).length;
};
const count = useMemo(countActiveUser, [users]);
return (
<div>
<h1>{`활성 사용자 수: ${count}`}</h1>
<button onClick={() => setIsOn((isOn) => !isOn)}>리랜더링 하기</button>
<button
onClick={() => {
setUsers(Array.from(users).concat({ active: true }));
}}
>
사용자 추가하기
</button>
</div>
);
};
2. useCallback()
useMemo(() => fn, deps) 와 useCallback(fn, deps) 는 같다.
-
컴포넌트가 리렌더링될 때 불필요하게 함수가 다시 생성되는 것을 방지한다.
-
함수 생성 자체가 오래 걸리는 경우, 함수를 자식 컴포넌트에 props로 넘겨줄 때 useCallback()을 사용하는 것이 좋다.
import { useState, useCallback } from 'react';
const App = () => {
const [firstName, setFirstName] = useState('junha');
const [lastName, setLastName] = useState('kim');
// 이름과 성이 바뀔 때마다 풀네임을 메모이제이션
const getfullName = useCallback(() => {
return `${firstName} ${lastName}`;
}, [firstName, lastName]);
return (
<>
{getfullName()}
<button onClick={() => setFirstName('준하')}>버튼</button>
</>
);
};
export default App;
3. useRef()
-
DOM Element에 접근 할 때 사용하며, **getElementById()**와 같은 기능을 한다.
-
DOM Element의 ref 속성을 이용한다.
import { useRef } from 'react';
const App = () => {
const inputRef = useRef(null);
const focusHandler = () => {
inputRef.current.focus();
};
const clickHandler = () => {
inputRef.current.value += '값이 바꼈어용';
};
return (
<div>
<input ref={inputRef} type='text' />
<button onClick={clickHandler}>값 바꾸기</button>
<button onClick={focusHandler}>input으로 포커스</button>
</div>
);
};
export default App;
useRef의 리턴 값
-
useRef의 리턴값에 current 속성을 이용하여 DOM을 컨트롤 할 수 있다.
-
이 값은 컴포넌트 생애 주기 내에서 유지한다.
-
useRef 객체가 변경되어도 컴포넌트가 재렌더링되지 않는다.

4. useTitle
: title 태그를 업데이트하는 기능
import { useEffect, useState } from "react";
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>Hi</div>;
};
export default App;
5. useClick
: 해당 element를 클릭 시 주어진 callBack함수 출력하기
: useRef() 사용
import { useEffect, useState, useRef } from "react";
const useClick = (onClick) => {
const element = useRef();
useEffect(() => {
if (element.current) {
element.current.addEventListener("click", onClick);
}
return () => {
element.current.removeEventListener("click", onClick);
};
});
return element;
};
const App = () => {
const sayHello = () => console.log("그걸 누르누");
const title = useClick(sayHello);
return (
<div className="APP">
<h1 ref={title}>Click me</h1>
</div>
);
};
export default App;
6. useConfirm
: 메세지를 확인시켜주고, 응답에 따라 콜백함수를 호출해주는 기능
: 사용자가 버튼클릭 작업 전 메세지 보여주고 싶을 때
const useConfirm = (message = "", callback, rejection) => {
if (!callback || typeof callback !== "function") {
return;
}
const confirmAction = () => {
if (confirm(message)) {
callback();
} else {
rejection();
}
};
return confirmAction;
};
const App = () => {
const deleteWorld = () => console.log("Deleting the World");
const abort = () => console.log("Aborted");
const confirmDelete = useConfirm(
"Are you sure to delete the World?",
deleteWorld,
abort
);
return (
<div className="APP">
<button onClick={confirmDelete}>Delete the World</button>
</div>
);
};
export default App;
7. 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 };
};
const App = () => {
const { enablePrevent, disablePrevent } = usePreventLeave();
return (
<div className="APP">
<button onClick={enablePrevent}>Protect</button>
<button onClick={disablePrevent}>Unprotect</button>
</div>
);
};
export default App;
beforeunload
: allows you to execute a function before the window gets closed, or before the person leaves your page
8. useBeforeLeave
: 마우스가 페이지를 벗어나면 실행시키는 기능
Cleanup Function
useEffect에서 return한 함수는 componentWillUnmount 때 호출된다.
이벤트리스너가 호출되거나 업데이트될 때마다 이벤트리스너가 add되는걸 원하는게 아니다. 고로 dependency는 []로 didmount될때한번만 호출하고, willunmount될 때 클린업함수로 리턴 함수로 써주자
import { useEffect } from "react";
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 sayHello = () => console.log("떠나지마용");
useBeforeLeave(sayHello);
return (
<div className="APP">
<h1>Hello</h1>
</div>
);
};
export default App;
9. useFadein
component의 reference를 얻는 방법이 특이하다.
useXXX함수에 useRef()를 설정하고 다시 element return하는 방식으로 해당 element를 조작한다. 이 방법이 효율적인 이유는 뭘까? : 예를들어 fadein효과를 주기 위해서, const el = useFadeIn()을 썼다고 해보자. 그럼 fadein효과를 주고 싶은 태그에 ref={el}만 넣으면 바로 그 element를 조작할 수 있다. 하나 알아둬야할 것은 html요소를 조작하기 위해 js부분은 함수로 모듈화시키고, 적용할 때는 html태그에 미리 선언해둔 변수이름만 prop에 넣으면 된다는 것. 이 디자인패턴이 효율적으로 보이긴한다. 왜냐면 vanilia js에서 작동시킬 때는 정해진 규격이 없어서 산발적으로 써져있었기 때문이다.
import { useEffect, useRef } from "react";
const useFadeIn = (duration = "1") => {
const element = useRef();
useEffect(() => {
if (element.current) {
const { current } = element;
current.style.transition = `opacity ${duration}s`;
current.style.opacity = 1;
}
}, []);
return { ref: element, style: { opacity: 0 } };
};
const App = () => {
const fadeIn3s = useFadeIn(2);
const fadeIn7s = useFadeIn(7);
return (
<div className="APP">
<h1 {...fadeIn3s}>Hello</h1>
<h5 {...fadeIn7s}>MR. JUNHA</h5>
</div>
);
};
export default App;
10. useNetwork
import { useEffect, useState } from "react";
const useNetwork = (onChange) => {
const [status, setStatus] = useState(navigator.onLine);
const handleChange = () => {
setStatus(navigator.onLine);
onChange(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 ? "Online" : "Offline");
};
const onLine = useNetwork(handleNetworkChange);
return (
<div className="APP">
<h1>{onLine ? "Online" : "Offline"}</h1>
</div>
);
};
export default App;
navigator.onLine
- Tells you true or false if your website is online
It's always good to think "maybe people wouldn't just do this" when you're making a library for other people
11. useNotification
import { useEffect, useState, useRef } 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;
};
const App = () => {
const triggerNotif = useNotification("Enter Summoner", {
body: "Explore the World that you dream"
});
return (
<div className="APP">
<button onClick={triggerNotif}>Hello</button>
</div>
);
};
export default App;
12. useAxios
**: about httpRequest
**
import React from "react";
import useAxios from "./useAxios";
const App = () => {
const { loading, data, error, refetch } = useAxios({
url: "https://yts.mx/api/v2/list_movies.json"
});
console.log(
`Loading: ${loading}\nError:${error}\nData:${JSON.stringify(data)}`
);
return (
<div>
<h1>{data && data.status}</h1>
<h2>{loading && "Loading"}</h2>
<button onClick={refetch}>Refetch</button>
</div>
);
};
export default App;
import defaultAxios from "axios";
import { useState, useEffect } 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;
More about Hooks
- https://ko.reactjs.org/docs/hooks-reference.html
- useContext ⭐
- useReducer ⭐
- useCallback ⭐
- useMemo ⭐
- useImperativeHandle
- useLayoutEffect
- useDebugValue
More about
https://sangcho.tistory.com/entry/ReactHooks%EC%9D%98%EB%B9%99%EC%82%B0