You will learn
- ์ด๋ฒคํธ ํธ๋ค๋ฌ์ effect ์ค์ ์ ํํ๋ ๋ฐฉ๋ฒ
- effect๊ฐ ๋ฐ์ํ์ด๊ณ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ๊ทธ๋ ์ง ์์ ์ด์
- effect์ ์ผ๋ถ ์ฝ๋๊ฐ ๋ฐ์ํ์ด์ง ์๊ธธ ๋ฐ๋ ๋ ํ ์ ์๋ ๋ฐฉ๋ฒ๋ค
- effect event๋ฅผ ์ ์ํ๊ณ effect ์์ ๋ถ๋ฆฌํ๋ ๋ฐฉ๋ฒ
- effect event๋ฅผ ํตํด effect์์ ์ต์ ์ props๋ state๋ฅผ ์ฝ๋ ๋ฐฉ๋ฒ
์ด๋ฒคํธ ํธ๋ค๋ฌ vs ์ดํํธ
์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ๋ง ๊ทธ๋๋ก ์ฌ์ฉ์ ์ด๋ฒคํธ์ ์ํด ์ด๋ฐ๋์ด์ ์คํ๋๋ ์ฝ๋๊ณ , ์ดํํธ๋ ๋๊ธฐํ synchronization์ด ํ์ํ ๋ ๋ง๋ค ์คํ๋๋ ์ฝ๋๋ค.
Reactive values ๋ฐ์ํ ๊ฐ
Props, State, ๊ทธ๋ฆฌ๊ณ ์ปดํฌ๋ํธ ์์์ ์ ์๋ ๋ณ์๋ค์ ๋ฐ์ํ ๊ฐ reactive value ๋ผ๊ณ ํ๋ค. ์ด ์ธ๊ฐ์ง๋ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง ๋ ๋ ๋ง๋ค ๋ณํ๊ธฐ ๋๋ฌธ์ด๋ค.
์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์ดํํธ๋ฅผ ๊ตฌ๋ถํ๋ ๊ธฐ์ค๋ ์ด ๋ฐ์ํ reactive ์ฌ๋ถ๋ค.
์ด๋ฒคํธ ํธ๋ค๋ฌ ์์ ๋ก์ง์ ๋ฐ์ํ์ด ์๋๋ค. sendMessage(message) ์์ message ์ํ๊ฐ ๋ณํ๋๋ผ๋ ์คํ๋์ง ์์ง๋ง, ์คํ๋ ๋์๋ ๋ณํ ์ํ๋ฅผ ์ฝ์ ์ ์๋ค. (Event handlers can read reactive values without โreactingโ to their changes.)
๋ฐ๋ฉด ์ดํํธ์ ๋ก์ง์ ๋ฐ์ํ์ด๋ค. ์ฆ ๋ฐ์ํ ๊ฐ๋ค(์์ฑ, ์ํ, ์ปดํฌ๋ํธ ์์์ ์ ์๋ ๋ณ์)์ด ๋ณ๊ฒฝ๋๋ฉด ํจ๊ป ์คํ๋๋ค.
์ดํํธ์์ ๋น๋ฐ์ํ ๋ก์ง ์ ๊ฑฐํ๊ธฐ
๋ฐ๋ผ์ ์ดํํธ ์์๋ ์ดํํธ๊ฐ ์์กดํ๊ณ ์๋ ๊ฐ์ ๋ฐ์ํ๋ ๋ฐ์ํ ๋ก์ง๋ค๋ง ์กด์ฌํด์ผ ํ๋ค.
ํ์ง๋ง ๊น๋ค๋ก์ด ๊ฒฝ์ฐ๊ฐ ์กด์ฌํ๋๋ฐ, ๊ฐ๋ น ์ฑํ
์๋ฒ ์
์ฅ์ ์ฑํ
๋ฐฉ ์
์ฅ์ ์๋ฆฌ๋ ์๋ฆผ์ ํ์ํ๊ณ ์ถ๋ค๋ฉด ์์ฐ์ค๋ฝ๊ฒ useEffect์์ ์์ฑํ๊ฒ ๋ ๊ฒ์ด๋ค.
function ChatRoom({ roomId, theme }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
showNotification('Connected!', theme);
});
connection.connect();
// ...
์ฌ์ฉ์๊ฐ ์ค์ ํ ui ํ
๋ง์ ๋ง๋ ์๋ฆผ์ฐฝ์ ๋์ฐ๊ธฐ ์ํด theme ์์ฑ์ showNotification ํจ์์ ์ธ์๋ก ์ ๋ฌํ๋๋ฐ, ์ด๋ ๊ฒ ์์ฑํ๋ ๊ฒฝ์ฐ theme ์ ์์กด์ฑ ๋ฐฐ์ด์ ๋ฃ์ด์ผ ํ๋ค.
๊ทธ๋ฐ๋ฐ ์ด๋ ๊ฒ ํ๋ฉด ์ฌ์ฉ์๊ฐ theme์ ๋ณ๊ฒฝํ ๋ ๋ง๋ค ์ดํํธ๊ฐ ์คํ๋๋ฉด์ ์ฑํ
์๋ฒ์ ๋ค์ ์ฐ๊ฒฐ๋๊ณ ์๋ฆผ์ฐฝ์ด ๋ ํ์๋ ๊ฒ์ด๋ค. ์ด๋ป๊ฒ theme์ ์์กด์ฑ ๋ฐฐ์ด์ ๋ฃ์ง ์์ผ๋ฉด์ ์ฑํ
๋ฐฉ ์
์ฅ์์๋ง ์๋ฆผ์ฐฝ์ ํ์ํ ์ ์์๊น?
Effect Event
์ด๋ฐ ๊ฒฝ์ฐ๋ฅผ ์ํด ๊ฐ๋ฐ๋ ํ
useEffectEvent๊ฐ ์์ด์ ์ด๊ฑธ ์ฌ์ฉํ๋ฉด ๋๋ค.
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme);
});
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
onConnected();
});
connection.connect();
return () => connection.disconnect();
}, [roomId]);
// ...
์ด๋ ๊ฒ ํ๋ฉด showNotification ํจ์๋ ์ต์ ์ํ์ theme์ ์ฌ์ฉํ ์ ์๊ณ , useEffect ๋ ์ ๋ง๋ก ํ์ํ ๊ฐ์๋ง ์์กดํ ์ ์๋ค.
function Page({ url }) {
const { items } = useContext(ShoppingCartContext);
const numberOfItems = items.length;
const onVisit = useEffectEvent(visitedUrl => {
logVisit(visitedUrl, numberOfItems);
});
useEffect(() => {
onVisit(url);
}, [url]);
// ...
}
์์ ๊ฐ์ ๊ฒฝ์ฐ์๋ ์ฌ์ฉ์๊ฐ ํ์ด์ง์ ๋ฐฉ๋ฌธํ ๋ ๋ง๋ค ์ฅ๋ฐ๊ตฌ๋ ์ ๋ณด์ ํจ๊ป ๋ก๊ทธ ๊ธฐ๋ก์ ๋จ๊ธธ ์ ์๋ค.
Effect Event ์ฌ์ฉ์ ์ฃผ์์ฌํญ
- ์ดํํธ ์์์๋ง ์ฌ์ฉํ๋ค.
- ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ ํ ์ ์ ๋ฌํ์ง ์๋๋ค.
function Timer() {
const [count, setCount] = useState(0);
const onTick = useEffectEvent(() => {
setCount(count + 1);
});
useTimer(onTick, 1000); // ๐ด Avoid: Passing Effect Events
return <h1>{count}</h1>
}
function useTimer(callback, delay) {
useEffect(() => {
const id = setInterval(() => {
callback();
}, delay);
return () => {
clearInterval(id);
};
}, [delay, callback]); // Need to specify "callback" in dependencies
}
์ฌ๊ธฐ์๋ onTick์ด๋ผ๋ ์ดํํธ ์ด๋ฒคํธ๋ฅผ ์ ์ํ๊ณ useTimer๋ผ๋ ํ
์ ์ ๋ฌํ๊ณ ์๋ค. ๊ทธ๋์ ํ
์ ์ดํํธ์์ callback์ด๋ผ๋ ์์ฑ์ ๋ฐ์ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ ์์กด์ฑ ๋ฐฐ์ด์ ์ถ๊ฐํด์ผ ํ๋๋ฐ, ์ด๋ ์ธ๋ชจ์๋ ์์กด์ฑ์ ์ถ๊ฐํ๋ ๊ฒ์ด๋ผ ์ข์ง ์์ ์ค๊ณ์ด๋ค.
function Timer() {
const [count, setCount] = useState(0);
useTimer(() => {
setCount(count + 1);
}, 1000);
return <h1>{count}</h1>
}
function useTimer(callback, delay) {
const onTick = useEffectEvent(() => {
callback();
});
useEffect(() => {
const id = setInterval(() => {
onTick(); // โ
Good: Only called locally inside an Effect
}, delay);
return () => {
clearInterval(id);
};
}, [delay]); // No need to specify "onTick" (an Effect Event) as a dependency
}
์ด๋ ๊ฒ callback์ผ๋ก ์ธ๋ถ์์ ํจ์๋ฅผ ์ฃผ์
๋ฐ๋๋ผ๋ ํ
์์์ ์ดํํธ ์ด๋ฒคํธ๋ฅผ ์ ์ํด์ผ ํ๋ค.
์์ ๋ฌธ์
2. ์ ๊น ๋ฉ์ถ๋ ์นด์ดํฐ
export default function Timer() {
const [count, setCount] = useState(0);
const [increment, setIncrement] = useState(1);
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + increment);
}, 1000);
return () => {
clearInterval(id);
};
}, [increment]);
์ดํํธ๊ฐ ๋ค์ ์คํ๋๋ฉด์ ํ์ด๋จธ๊ฐ ์๋กญ๊ฒ ์ค์ ๋๊ธฐ ๋๋ฌธ์ increment๋ฅผ ์กฐ์ ํ ๋ ์ ๊น ํ๋ฆฌ์ง๋๋ ํ์์ด ๋ฐ์ํ๋ค.
const onTick = useEffectEvent(() => {
setCount(c => c + increment);
});
useEffect(() => {
const id = setInterval(() => {
onTick();
}, 1000);
return () => {
clearInterval(id);
};
}, []);
์ด๋ ๊ฒ increment ์์กด์ฑ์ ์ ๊ฑฐํ๊ณ useEffectEvent ํ
์ ์ฌ์ฉํ๋ฉด ๋๋ค.
3. ๋๋ ์ด ์ค์ ์ด ์ ์ฉ ์๋๋ ์นด์ดํฐ
export default function Timer() {
const [count, setCount] = useState(0);
const [increment, setIncrement] = useState(1);
const [delay, setDelay] = useState(100);
const onTick = useEffectEvent(() => {
setCount(c => c + increment);
});
const onMount = useEffectEvent(() => {
return setInterval(() => {
onTick();
}, delay);
});
useEffect(() => {
const id = onMount();
return () => {
clearInterval(id);
}
}, []);
์ธํฐ๋ฒ ๋๋ ์ด๋ฅผ ๋ณ๊ฒฝํด๋ ์ ์ฉ์ด ๋์ง ์๋๋ค. ์ ์ฉ์ด ์๋๋ ์ด์ ๋ ์ดํํธ๊ฐ delay ๊ฐ์ ๋ฐ์ํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
const onTick = useEffectEvent(() => {
setCount(c => c + increment);
});
useEffect(() => {
const id = setInterval(() => {
onTick();
}, delay);
return () => {
clearInterval(id);
}
}, [delay]);
onMount ์ ๋ก์ง์ ์ดํํธ๊ฐ ๊ฐ์ ธ์ผํ๋ ๋ฐ์ํ ๋ก์ง์ด๋ค.
4. ์๋ฆผ์ฐฝ ๋๋ ์ด
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Welcome to ' + roomId, theme);
});
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
setTimeout(() => {
onConnected();
}, 2000);
});
connection.connect();
return () => connection.disconnect();
}, [roomId]);
์ฑํ ๋ฐฉ์ general -> travel -> music ๋ก ๋น ๋ฅด๊ฒ ๋ ๋ฒ ๋ณ๊ฒฝํ๋ฉด ์๋ฆผ์ฐฝ์ music ์๋ฆผ์ฐฝ ๋ ๊ฐ๊ฐ ๋ฌ๋ค. travel ํ๋ music ํ๋๊ฐ ๋จ๋๋ก ์์ ํด์ผ ํ๋ค.
์ด๊ฑด ๋ฐ๋๋ก ์ดํํธ ์ด๋ฒคํธ๋ก ๋ถ๋ฆฌํ๋ฉด์ ์ต์ roomId๋ฅผ ๊ฐ์ ธ์์ ๋ฐ์ํ๋ ๋ฌธ์ ๋ค.
ํ์ง๋ง ์ดํํธ ์์ ๋ฃ์ด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด theme๋ ์์กด์ฑ ๋ฐฐ์ด์ ๋ค์ด๊ฐ์ผ ํด์ ๋ถ๊ฐ๋ฅ.
onConnected๊ฐ ์ต์ rooomId๋ฅผ ์ฝ์ง ๋ชปํ๊ฒ ํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น? ๋ณด๋๊น ์ ๋ต์ ์ง์ onConnectedํจ์์ ์ธ์๋ก ๊ฑด๋ด์ฃผ๋ ๊ฒ์ด๋ค.
const onConnected = useEffectEvent(connectedRoomId => {
showNotification('Welcome to ' + connectedRoomId, theme);
});
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
setTimeout(() => {
onConnected(roomId);
}, 2000);
});
๊ทธ๋ฐ๋ฐ ์ถ๊ฐ๋ก ์๊ฐํด๋ณด๋ฉด ๊ณผ์ฐ ์๋ฆผ์ฐฝ์ ๊ตณ์ด ๋ ๋ฒ ๋ณด์ฌ์ค ํ์๊ฐ ์์๊น? ๋ฌธ์ ์์๋ ์ถ๊ฐ๋ก ๋๋ฐ์ด์ค๋ฅผ ์ ์ฉํด ๋ง์ง๋ง ์ ์ ์ฑ๋์ ๋ํ ์๋ฆผ๋ง ๋ณด์ฌ์ฃผ๋ ๋ฐฉ์์ ์๊ฐํ๋ค.
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
let notificationTimeoutId;
connection.on('connected', () => {
notificationTimeoutId = setTimeout(() => {
onConnected(roomId);
}, 2000);
});
connection.connect();
return () => {
connection.disconnect();
if (notificationTimeoutId !== undefined) {
clearTimeout(notificationTimeoutId);
}
};
}, [roomId]);

