아래 코드에서 ESLint가 경고문을 뱉었다.
useEffect(() => {
if (typeof window.ethereum !== 'undefined') {
getAccountAndNetwork();
window.ethereum.on('accountsChanged', getAccountAndNetwork);
window.ethereum.on('networkChanged', getAccountAndNetwork);
} else {
console.log('MetaMask가 설치되어 있지 않습니다.');
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
에러 로그
React Hook useEffect has missing dependencies: ... Either include them or remove the dependency array react-hooks/exhaustive-deps
useEffect의 의존성 배열에 외부 함수나 변수를 사용하였는데 의존성 배열에는 포함시키지 않는다면 useEffect가 외부 함수나 변수의 변경을 감지하지 못하기 때문에 ESLint 경고가 발생한다.
1. 외부 함수, 변수를 의존성 배열에 넣어주기
useEffect(() => {
if (typeof window.ethereum !== 'undefined') {
getAccountAndNetwork();
window.ethereum.on('accountsChanged', getAccountAndNetwork);
window.ethereum.on('networkChanged', getAccountAndNetwork);
} else {
console.log('MetaMask가 설치되어 있지 않습니다.');
}
}, [getAccountAndNetwork]);
2. 해당 함수, 변수를 useEffect Hook에서 정의하기
useEffect(() => {
const getAccountAndNetwork = () => {};
if (typeof window.ethereum !== 'undefined') {
getAccountAndNetwork();
window.ethereum.on('accountsChanged', getAccountAndNetwork);
window.ethereum.on('networkChanged', getAccountAndNetwork);
} else {
console.log('MetaMask가 설치되어 있지 않습니다.');
}
}, []);
3. 주석을 통해 경고를 무시
useEffect(() => {
if (typeof window.ethereum !== 'undefined') {
getAccountAndNetwork();
window.ethereum.on('accountsChanged', getAccountAndNetwork);
window.ethereum.on('networkChanged', getAccountAndNetwork);
} else {
console.log('MetaMask가 설치되어 있지 않습니다.');
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
만약 컴포넌트가 처음 렌더링될 때 외부 함수나 변수의 초기값을 통해 렌더링하고 싶고 그 이후 외부 함수나 변수가 변경되더라도 useEffect를 실행시키고 싶지 않은 경우, 의도적으로 외부 함수, 변수의 변화를 감지하지 못하는 상태를 유지하면 된다. 경고가 뜨는것은 짜증나기 때문에 경고 무시 주석을 사용한다.
1번 방법을 실행했을 경우, 아래와 같은 추가 오류 발생.
에러 로그
The '...' function makes the dependencies of useEffect Hook change on every render. Move it inside the useEffect callback. Alternatively, wrap the definition of '...' in its own useCallback() Hook react-hooks/exhaustive-deps
외부 함수나 변수들을 이들을 의존성 배열에 넣어주면 무한 렌더링 현상이 발생하기 때문에 에러가 발생한다.
컴포넌트가 렌더링될 때마다 컴포넌트 내부에 선언한 함수는 새로운 함수나 객체로 선언된다. 즉 useEffect로 렌더링이 일어나면 컴포넌트 내부의 함수는 새롭게 선언이 되고 이로 인해 useEffect는 또 다시 렌더링을 일으키는 현상이 무한 반복되는 것이다.
그래서 아래와 같이 useCallback의 메모이제이션 기능을 사용하여 함수를 다시 작성하였다.
const getAccountAndNetwork = useCallback(async () => {
try {
// Ethereum Provider API에서 eth_requestAccounts를 통해 계정 정보를 반환받음
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts',
});
console.log('Accounts:', accounts);
// 현재 로그인된 계정(index=0)을 상태에 저장
setWalletAddress(accounts[0]);
console.log('Wallet Address: ', walletAddress);
const addressShortcut = accounts[0].slice(0, 6) + '...' + accounts[0].slice(-4);
setShortenedAddress(addressShortcut);
// 현재 연결된 Ethereum 네트워크의 식별자를 가져와서 networkId 변수에 저장
const networkId = window.ethereum.networkVersion;
// 홈페이지에서 사용할 네트워크 이름을 식별자 별로 저장
const networkNames = {
1: 'Ethereum Mainnet',
137: 'Polygon Mainnet',
};
// 해당 network를 사용 중이라면 해당 네트워크의 이름을 띄움
const name = networkNames[networkId] || 'Unregistered network';
setNetworkName(name);
console.log('Current network name: ', name);
} catch (error) {
// 에러가 뜨면 계정, 네트워크 이름 초기화
setWalletAddress('');
setNetworkName('');
console.log('메타마스크에서 네트워크 연결이 필요합니다. ');
}
}, [walletAddress]);
useCallback의 메모이제이션 기능을 사용하면 의존성 배열에 있는 값이 변경되지 않는 한 컴포넌트가 리렌더링되더라도 동일한 함수 참조를 유지할 수 있어 무한 렌더링 현상이 사라진다.
다른 방법으로는 해당 함수를 컴포넌트 외부에 작성하거나, 의존성 배열 내부에 작성하는 방법이 있다.