๋คํธ์ํฌ์ ํฌ๊ฒ ์์กดํ๋ ์ฑ.
๋คํธ์ํฌ ์จ๋ผ์ธ ์ฌ๋ถ๋ฅผ ์ถ์ , ์จ์คํ๋ผ์ธ ์ด๋ฒคํธ๋ฅผ ๊ตฌ๋
ํ๊ณ ํด๋น ์ํ๋ฅผ ์
๋ฐ์ดํธํ๋ ์ดํํธ๋ฅผ ์ด์ฉํ
์๋์ ๊ฐ์ ์์๋ฅผ ๋ณผ ์ ์๋ค.
Copy import { useState, useEffect } from 'react';
export default function StatusBar() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return <h1>{isOnline ? 'โ
Online' : 'โ Disconnected'}</h1>;
}
๊ทธ๋ฆฌ๊ณ ์จ๋ผ์ธ ์ํ์ธ์ง ์ถ์ ํด ๋ฒํผ์ text๊ฐ ๋ฐ๋๋ ๋ฒํผ๋ ์๋ค.
Copy import { useState, useEffect } from 'react';
export default function SaveButton() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
function handleSaveClick() {
console.log('โ
Progress saved');
}
return (
<button disabled={!isOnline} onClick={handleSaveClick}>
{isOnline ? 'Save progress' : 'Reconnecting...'}
</button>
);
}
์์ ์ํํ์ ์ปดํฌ๋ํธ, ๋ฒํผ ์ปดํฌ๋ํธ๋ ์๋ก ๋ณด์ด๋ ๋ชจ์์ ๋ค๋ฅด์ง๋ง ๋์ผํ ๋ก์ง์ด ๋ฐ๋ณต๋๋ ๊ฒ์ด๋ค.
์ด๋ฐ ๊ฒฝ์ฐ
useOnlineStatus
ํ
์ด ์์ผ๋ฉด ๋ ์ปดํฌ๋ํธ๋ฅผ ๋จ์ํ ํ ์ ์๋ค.
Copy function StatusBar() {
const isOnline = useOnlineStatus();
return <h1>{isOnline ? 'โ
Online' : 'โ Disconnected'}</h1>;
}
function SaveButton() {
const isOnline = useOnlineStatus();
return (
<button disabled={!isOnline} onClick={handleSaveClick}>
{isOnline ? 'Save progress' : 'Reconnecting...'}
</button>
);
}
isOnline state์ useEffect๊น์ง ์ฎ๊ฒจ์ ์ปค์คํ
ํ
์ ๋ง๋ค ์ ์๋ค.
Copy function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
hook ๋ช
๋ช
๊ท์น
custom hook์ use๋ก ์์ํด์ผํ๋ค. (useOnlineStatus, useUserState, use์ด์ฉ๊ณ )
lint ์ค์ ์ด react๋ก ๋์ด์์ผ๋ฉด ์์์ ๋ค๋ฅธ ํ
์ ์ฌ์ฉํ์ง ๋ชปํ๋๋ก ๋ง์์ฃผ๊ธฐ๊น์ง ํ๋ค๊ณ ํจ.
์ปค์คํ
ํ
์ ์ฌ์ฉํ๋ฉด ์ํ ์์ฒด๊ฐ ์๋ ์ํ ์ ์ฅ ๋ก์ง์ ๊ณต์ ํ ์ ์์ต๋๋ค
์์ ์์ ์์๋ ๋คํธ์ํฌ๋ฅผ ์ผ๊ณ ๋ ๋ ๋ ์ปดํฌ๋ํธ๊ฐ ํจ๊ป ์
๋ฐ์ดํธ ๋์์ต๋๋ค.
๊ทธ๋ฌ๋ ๋จ์ผ isOnline
์ํ ๋ณ์๊ฐ ๋ ์ปดํฌ๋ํธ ๊ฐ์ ๊ณต์ ๋๋ค๊ณ ์๊ฐํ๋ ๊ฒ์ ์๋ชป๋ ์๊ฐ์
๋๋ค.
Copy function StatusBar() {
const isOnline = useOnlineStatus();
// ...
}
function SaveButton() {
const isOnline = useOnlineStatus();
// ...
}
--------------------------------------
function StatusBar() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
// ...
}, []);
// ...
}
function SaveButton() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
// ...
}, []);
// ...
}
์์ ๋๊ฐ๋ค.
์ด ๋ ๊ฐ์ง ์ํ ๋ณ์์ ์ดํํธ๋ ์์ ํ ๋
๋ฆฝ์ ์ธ ์ํ ๋ณ์
์
๋๋ค.
๋คํธ์ํฌ๊ฐ ์ผ์ ธ ์๋์ง ์ฌ๋ถ์ ๊ด๊ณ์์ด ๋์ผํ ์ธ๋ถ ๊ฐ์ผ๋ก ๋๊ธฐํํ๊ธฐ ๋๋ฌธ์ ๋์์ ๋์ผํ ๊ฐ์ ๊ฐ๊ฒ ๋ ๊ฒ๋ฟ์
๋๋ค.
Copy import { useState } from 'react';
export default function Form() {
const [firstName, setFirstName] = useState('Mary');
const [lastName, setLastName] = useState('Poppins');
function handleFirstNameChange(e) {
setFirstName(e.target.value);
}
function handleLastNameChange(e) {
setLastName(e.target.value);
}
return (
<>
<label>
First name:
<input value={firstName} onChange={handleFirstNameChange} />
</label>
<label>
Last name:
<input value={lastName} onChange={handleLastNameChange} />
</label>
<p><b>Good morning, {firstName} {lastName}.</b></p>
</>
);
}
Copy import { useState } from 'react';
export function useFormInput(initialValue) {
const [value, setValue] = useState(initialValue);
function handleChange(e) {
setValue(e.target.value);
}
const inputProps = {
value: value,
onChange: handleChange
};
return inputProps;
}
๊ฐ์ด๋ผ๋ ์ํ ๋ณ์๋ฅผ ํ๋๋ง ์ ์ธํ๋ค.
์ด ํ
์ Form ์ปดํฌ๋ํธ์์ ๋ ๋ฒ ํธ์ถํด๋ณด์.
Copy function Form() {
const firstNameProps = useFormInput('Mary');
const lastNameProps = useFormInput('Poppins');
// ...
๊ฐ์ ํ
์ ๋ ๋ฒ ํธ์ถํ๋ฉด ๋๊ฐ์ state๋ฅผ ์ ์ธํ ๊ฒ ์ฒ๋ผ ์๋ํ๋ค.
ํ
์ผ๋ก ์์ฑํ ์ธ์คํด์ค๋ก ์๊ฐํ๋ ์ดํด๊ฐ ์ฝ๋ค. ๊ฐ๊ฐ์ state๋ฅผ ๊ฐ๋.
!! ํต์ฌ : ์ํ ๋ก์ง์ ๊ณต์ ํ๋ ๊ฑฐ์ง ์ํ ์์ฒด์ ๊ณต์ ๊ฐ ์๋๋ผ๋ ์ !!
์ํ ์์ฒด๋ฅผ ๊ณต์ ํ๊ณ ์ถ์ผ๋ฉด ๋ถ๋ชจ๋ก ์ฌ๋ ค์ ๊ณต์ ํ๋ผํจ.
Copy import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';
import { showNotification } from './notifications.js';
export default function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.on('message', (msg) => {
showNotification('New message: ' + msg);
});
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]);
return (
<>
<label>
Server URL:
<input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />
</label>
<h1>Welcome to the {roomId} room!</h1>
</>
);
}
Copy export function useChatRoom({ serverUrl, roomId }) {
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.connect();
connection.on('message', (msg) => {
showNotification('New message: ' + msg);
});
return () => connection.disconnect();
}, [roomId, serverUrl]);
}
Copy export default function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useChatRoom({
roomId: roomId,
serverUrl: serverUrl
});
return (
<>
<label>
Server URL:
<input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />
</label>
<h1>Welcome to the {roomId} room!</h1>
</>
);
}
์ด๋ ๊ฒ ํ๋ฉด ChatRoom ์ปดํฌ๋ํธ๊ฐ ๋ด๋ถ์์ ์ด๋ป๊ฒ ์๋ํ๋์ง์ ๋ํ ๊ฑฑ์ ์์ด
์ฌ์ฉ์ ์ง์ Hook์ ํธ์ถํ ์ ์๋ค.
๋ก์ง์ด ์ฌ์ ํ props์ state ๋ณ๊ฒฝ์ ๋ฐ์ํ๋ฉด์ ์ฝ๋๋ ํจ์ฌ ๊น๋ํด์ง๋ค.
when to use custom hooks
์ปค์คํ
Hook์ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ
์ค๋ณต๋๋ ๋ชจ๋ ์ฝ๋์ ๋ํด ์ฌ์ฉ์ ์ ์ Hook์ ์ถ์ถํ ํ์๋ ์๋ค.
ํ์ง๋ง Effect๋ฅผ ์์ฑํ ๋๋ง๋ค ์ปค์คํ
Hook์ผ๋ก ๊ฐ์ธ๋ ๊ฒ์ด ๋ ๋ช
ํํ ์ง ๊ณ ๋ คํ๋ผ๊ณ ํจ.
Copy function ShippingForm({ country }) {
const [cities, setCities] = useState(null);
// This Effect fetches cities for a country
useEffect(() => {
let ignore = false;
fetch(`/api/cities?country=${country}`)
.then(response => response.json())
.then(json => {
if (!ignore) {
setCities(json);
}
});
return () => {
ignore = true;
};
}, [country]);
const [city, setCity] = useState(null);
const [areas, setAreas] = useState(null);
// This Effect fetches areas for the selected city
useEffect(() => {
if (city) {
let ignore = false;
fetch(`/api/areas?city=${city}`)
.then(response => response.json())
.then(json => {
if (!ignore) {
setAreas(json);
}
});
return () => {
ignore = true;
};
}
}, [city]);
// ...
์ด๋ฐ ์ฝ๋๋ ์๋นํ ๋ฐ๋ณต์ ์ด๋ค. ์ด๋ฐ ํจ๊ณผ๋ ์๋ก ๋ถ๋ฆฌํด ์ ์งํ๋๊ฒ์ด ๋ง๋ค.
์๋ก ๋ค๋ฅธ ๋ ๊ฐ์ง๋ฅผ ๋๊ธฐํํ๋ฏ๋ก ํ๋์ effect๋ก ๋ณํฉํ๋ฉด ์๋๋ค.
์ด๋ฌํ ํ
์ผ๋ก ๋บ๋ค.
Copy function useData(url) {
const [data, setData] = useState(null);
useEffect(() => {
if (url) {
let ignore = false;
fetch(url)
.then(response => response.json())
.then(json => {
if (!ignore) {
setData(json);
}
});
return () => {
ignore = true;
};
}
}, [url]);
return data;
}
Copy function ShippingForm({ country }) {
const cities = useData(`/api/cities?country=${country}`);
const [city, setCity] = useState(null);
const areas = useData(city ? `/api/areas?city=${city}` : null);
// ...
custom hook์ ์ถ์ถํ๋ฉด ๋ฐ์ดํฐ ํ๋ฆ์ ๋ช
์์ ์ผ๋ก ๋ง๋ค ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ Effect๋ฅผ ์์ผ๋ก ์จ๊ธฐ๊ฒ ๋์ด ๋ถํ์ํ ์ข
์์ฑ์ ์ถ๊ฐํ๋๊ฒ์ ๋ฐฉ์งํ ์ ์๋ค.
์ด์์ ์ผ๋ก๋ ์๊ฐ์ด ์ง๋๋ฉด ์ฑ์ effect ๋๋ถ๋ถ์ด ์ปค์คํ
ํ
์ ํฌํจ๋ ๊ฒ์ด๋ผํจ.
Custom Hooks help you migrate to better patterns
effect๋ "ํ์ถ๊ตฌ"์
๋๋ค. "React๋ฅผ ๋ฒ์ด๋์ผ ํ ๋", ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ ์ฌ๋ก์ ๋ ๋์ ๋ด์ฅ ์๋ฃจ์
์ด ์์ ๋ ์ฌ์ฉํฉ๋๋ค.
์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ React ํ์ ๋ชฉํ๋ ๋ ๊ตฌ์ฒด์ ์ธ ๋ฌธ์ ์ ๋ํ ๋ ๊ตฌ์ฒด์ ์ธ ์๋ฃจ์
์ ์ ๊ณตํจ์ผ๋ก์จ ์ฑ์ Effect ์๋ฅผ ์ต์ํ์ผ๋ก ์ค์ด๋ ๊ฒ์
๋๋ค.
effect๋ฅผ ์ปค์คํ
Hook์ผ๋ก ๊ฐ์ธ๋ฉด ์ด๋ฌํ ์๋ฃจ์
์ด ์ ๊ณต๋ ๋ ์ฝ๋๋ฅผ ๋ ์ฝ๊ฒ ์
๊ทธ๋ ์ด๋ํ ์ ์์ต๋๋ค.
Copy import { useOnlineStatus } from './useOnlineStatus.js';
function StatusBar() {
const isOnline = useOnlineStatus();
return <h1>{isOnline ? 'โ
Online' : 'โ Disconnected'}</h1>;
}
function SaveButton() {
const isOnline = useOnlineStatus();
function handleSaveClick() {
console.log('โ
Progress saved');
}
return (
<button disabled={!isOnline} onClick={handleSaveClick}>
{isOnline ? 'Save progress' : 'Reconnecting...'}
</button>
);
}
export default function App() {
return (
<>
<SaveButton />
<StatusBar />
</>
);
}
Copy import { useState, useEffect } from 'react';
export function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
์ ์ฝ๋์๋ ์ฃ์ง ์ผ์ด์ค๊ฐ ์๋ค.
์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ ๋ ๋, isOnline์ด ๊ธฐ๋ณธ์ ์ผ๋ก true์ง๋ง
๋คํธ์ํฌ๊ฐ ์ด๋ฏธ ์คํ๋ผ์ธ ์ํ๋ผ๋ฉด ํ๋ฆด ์ ์๋ค.
๋ธ๋ผ์ฐ์ navigator.onLine API๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฅผ ํ์ธํ ์ ์์ง๋ง
์๋ฒ์์ React ์ฑ์ ์คํํ์ฌ ์ด๊ธฐ html์ ์์ฑํ๋ ๊ฒฝ์ฐ,
์ด๋ฅผ ์ง์ ์ฌ์ฉํ๋ฉด ์ค๋จ๋ ์ ์๋ค. ์ด๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํด
React 18์๋ useSyncExternalStore๋ผ๋
์ ์ฉ API๊ฐ ํฌํจ๋์ด ์๋ค.
Copy import { useSyncExternalStore } from 'react';
function subscribe(callback) {
window.addEventListener('online', callback);
window.addEventListener('offline', callback);
return () => {
window.removeEventListener('online', callback);
window.removeEventListener('offline', callback);
};
}
export function useOnlineStatus() {
return useSyncExternalStore(
subscribe,
() => navigator.onLine, // How to get the value on the client
() => true // How to get the value on the server
);
}
์ปค์คํ
Hook์ผ๋ก effect๋ฅผ ๋ํํ๋ ๊ฒ์ด ์ข
์ข
์ ์ตํ ๋ ๋ค๋ฅธ ์ด์ ๋ค.
์ดํํธ๋ฅผ ์ค๊ฐ๋ ๋ฐ์ดํฐ ํ๋ฆ์ ๋งค์ฐ ๋ช
ํํ๊ฒ ๋ง๋ค ์ ์๋ค.
์ปดํฌ๋ํธ๊ฐ Effect์ ์ ํํ ๊ตฌํ๋ณด๋ค๋ ์๋์ ์ง์คํ ์ ์๋ค.
React์ ์๋ก์ด ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋๋ฉด ์ปดํฌ๋ํธ๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ๋ ํด๋น ํจ๊ณผ๋ฅผ ์ ๊ฑฐํ ์ ์๋ค.