useEffectiveEvent() 훅은 현재 experimental 버전에서만 지원합니다.
const onSomething = useEffectEvent(callback)
sideEffect를 가지고 있으면서 개발자가 원치 않는 상황에서 리렌더링해야하는 문제를 해결하기 위한 hook이다.
아래 코드는 url이 변화할 때마다 새로운 채팅방에 연결하는 컴포넌트다.
import React from 'react';
export function ChatRoom({ url, loggingOptions }) {
function onConnected(url) {
logConnnection(`Connected to ${url}`);
}
React.useEffect(() => {
const room = connnectToRoom(url);
room.onConnected(() => {
onConnected(url);
})
return () => {
room.disconnect();
}
}, [url])
return null;
}
그러나 onConnnected에 props로 받은 loggingOptions를 추가한다면...
import React from 'react';
export function ChatRoom({ url, loggingOptions }) {
/* onConnected함수에 loggingOptions를 추가하면 missing dependancy 경고 출력 */
function onConnected(url) {
logConnnection(`Connected to ${url}, loggingOptions`);
}
React.useEffect(() => {
const room = connnectToRoom(url);
room.onConnected(() => {
onConnected(url);
})
return () => {
room.disconnect();
}
/* 때문에 의존성 배열에 onConnected를 추가해야한다. */
}, [url, onConnected])
return null;
}
이제 새로운 문제는 컴포넌트가 갱신될 때마다 onConnected함수가 새로 선언된다는 점이다. 이럴 때의 해결법은 보통 두 가지인데, 두 가지 모두 별개의 잠재적인 문제점이 있다. 하나씩 살펴보자
import React from 'react';
/* 1. onConnected함수를 useEffect내부로 옮기기 */
export function ChatRoom({ url, loggingOptions }) {
React.useEffect(() => {
function onConnected(url) {
logConnnection(`Connected to ${url}, loggingOptions`);
}
const room = connnectToRoom(url);
room.onConnected(() => {
onConnected(url);
})
return () => {
room.disconnect();
}
/*
* 이제 depencyarray에 loggingOptions을 요구한다.
* 문제는, 프로그래머는 loggingOptions가 변경될 때마다 채팅방에 다시 연결할 의도가 없다는 점이다.
*/
}, [url])
return null;
}
import React from 'react';
/* 2. useCallback으로 onConnected함수를 감싸기 */
export function ChatRoom({ url, loggingOptions }) {
/*
* 같은 문제에 봉착한다. loggingOptions가 변화할 때마다 onConnected함수가 바뀌고
*/
const onConnected = React.useCallback((url) => {
logConnnection(`Connected to ${url}, loggingOptions`);
}, [loggingOptions])
React.useEffect(() => {
const room = connnectToRoom(url);
room.onConnected(() => {
onConnected(url);
})
return () => {
room.disconnect();
}
/*
* 그리고 onConnected가 바뀌었으므로 useEffect가 동작한다.
*/
}, [url, onConnected])
return null;
}
import React, { experimental_useEffectEvent as useEffectEvent } from 'react';
export function ChatRoom({ url, loggingOptions }) {
const onConnected = useEffectEvent(url => {
logConnnection(`Connected to ${url}, loggingOptions`);
})
React.useEffect(() => {
const room = connnectToRoom(url);
room.onConnected(() => {
onConnected(url);
})
return () => {
room.disconnect();
}
}, [url])
return null;
}
현재 useEffectEvent의 콜백함수에서는 loggginOptions를 파라미터로 받고 있지 않다. 만약 url도 제거하면 어떻게 될까?
import React, { experimental_useEffectEvent as useEffectEvent } from 'react';
export function ChatRoom({ url, loggingOptions }) {
/*
* 다음과 같은 코드라도 동작은 하지만 잠재적인 문제의 여지가 있다.
* 만약 아주 빠른 속도로 여러번 전환한다면 (before = url, after = newUrl)
* props로 전달받은 loggingOptions값은 항상 최신 상태로 유지되므로
* 컴포넌트가 렌더링되는 와중에 다수의 요청이 모두 newUrl으로 진행될 가능성이 있다.
* 실제 useEffect에서 Sync를 맞춰야 하는 값이 있다면 반드시 명시적으로 콜백함수에 넘겨주자.
*/
const onConnected = useEffectEvent(() => {
logConnnection(`Connected to ${url}, loggingOptions`);
})
React.useEffect(() => {
const room = connnectToRoom(url);
room.onConnected(() => {
onConnected();
})
return () => {
room.disconnect();
}
}, [url])
return null;
}