Suspense와 ErrorBoundary를 하나로 합치기
2021년 10월 22일
데이터를 가져오는 컴포넌트를 쓸 때마다 두 가지로 감싸야 한다.
function Main() {
return (
<ErrorBoundary fallback={<div>에러 발생!</div>}>
<Suspense fallback={<div>로딩 중...</div>}>
<UserInfo />
</Suspense>
</ErrorBoundary>
);
}
로딩 처리는 Suspense에, 에러 처리는 ErrorBoundary에 위임하는 구조다. 매번 이 두 컴포넌트를 함께 쓴다면 하나로 합치는 게 낫다.
리셋 기능을 갖춘 ErrorBoundary
React 공식 문서의 ErrorBoundary는 에러가 난 뒤 상태를 리셋하는 기능이 없다. 재시도 버튼을 만들려면 resetError 메서드를 직접 추가해야 한다.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
this.resetError = this.resetError.bind(this);
}
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error(error, errorInfo);
}
resetError() {
this.setState({ hasError: false });
}
render() {
if (this.state.hasError) {
return this.props.fallback(this.resetError);
}
return this.props.children;
}
}
fallback을 함수로 받아 resetError를 넘겨준다. 사용하는 쪽에서 재시도 버튼을 자유롭게 구성할 수 있다.
하나로 합치기
ErrorBoundary 안에 Suspense를 포함한 AsyncBoundary를 만든다.
function AsyncBoundary({ errorFallback, loadingFallback, children }) {
return (
<ErrorBoundary fallback={errorFallback}>
<Suspense fallback={loadingFallback}>
{children}
</Suspense>
</ErrorBoundary>
);
}
이제 사용하는 쪽은 하나만 쓰면 된다.
function Main() {
return (
<AsyncBoundary
loadingFallback={<div>로딩 중...</div>}
errorFallback={(reset) => (
<div>
에러 발생! <button onClick={reset}>다시 시도</button>
</div>
)}
>
<UserInfo />
</AsyncBoundary>
);
}
정리
Suspense와 ErrorBoundary는 항상 같이 쓰인다. 둘을 매번 따로 감싸는 건 중복이다. AsyncBoundary로 합치면 선언부가 줄고, 재시도 로직도 한 곳에서 관리할 수 있다.