이 자료가 도움이 많이 되었음!
promise를 던지면 suspense가 fallback에 할당된 컴포넌트를 보여준다
promise가 아닌 값을 return 하면 fallback대신 Suspense 내부에 children으로 들어가있는 컴포넌트를 보여준다.
대신 console 찍어보면 fetch 하는 함수 내부를 무한루프로 돌면서 promise를 계속해서 던짐
또한 위 블로그를 보면 아는데 어쩃든 children 컴포넌트를 먼저 마운트 시키고 그 내부 로직을 실행시키다가 fetch 함수를 만나면 실행시키고 그 안에서 promise를 던지게 되면 Suspense가 fallback을 보여주고 값을 return 하게 되면 다시 children을 마운트 시키는 형식이다!
useRef로 실험한 내용 보면됨!
대부분의 예시는 아래처럼 함수 위에 값을 선언해서 계속해서 호출되더라도 값이 날라가지 않도록 한 뒤에 data fetching이 끝나면 data에 값을 할당하고 data에 값이 할당되면 return 하는 식이었음
let data;
export function useFetchSuspense<T>({ fetcher, key }: Props<T>) {
...
}
근데 우리는 캐싱을 구현했으니까 fetch의 결과를 캐시하고 만약 캐시된 값이 있으면 반환하는 식으로 하면 되지 않을까 해서 구현을 해봤고 결론적으로는 성공!
우리 코드
export function useFetchSuspense<T>({ fetcher, key }: Props<T>) {
const promise = useRef<Promise<ResponseProps<any>>>();
const { getCache, setCache } = useCache();
const value = getCache({ key }) || { status: 'new', data: null, dataUpdatedAt: new Date() };
if (value.status === 'rejected') {
throw new Error(value.status);
}
if (value.status === 'resolved') {
return value.data as T;
}
function fetchData() {
promise.current = (async () => {
return fetcher();
})();
promise.current
.then(response => {
setCache({
key,
value: {
status: 'resolved',
data: response.data,
dataUpdatedAt: new Date(),
},
});
})
.catch(error => {
setCache({
key,
value: {
status: 'rejected',
data: null,
dataUpdatedAt: new Date(),
},
});
throw new Error(error);
});
throw promise.current;
}
fetchData();
throw promise.current;
}
사용 예시
이렇게 layout 단에서 크게 suspense로 묶으면 선언적으로 전체 fetching에 대한 로딩을 한번에 선언해 줄수도 있고
MyCarLayout.tsx