2023. 7. 26. 15:12ㆍreact
React 로 만들어진 페이지에서 게시판이나 카테고리의 글 등이 나타나기 전
"로딩중..."
또는 이미지로 progress bar 가 나타나는 경우가 존재한다.
데이터를 서버로 부터 받아오기 전 비어있는 페이지, 또는 데이터를 받아온 후 레이아웃이 변경되며 페이지가 전체적으로 덜컥거리는 현상을 React Suspension 로 감출 수 있다.
일반적으로 데이터를 불러와 component 에 뿌리고 싶을 때 일반적으로 아래와 같이 코드를 작성한다.
const [work, loading] = fetch data('/work')
if (loading) {
return <Spinner />
}
return <Work data={work} />
loading 변수에 지정된 값이 true 라면 로딩중을 나타내는 식으로 말이다.
이부분을 React 의 Suspense 로 최적화가 가능하다.
const work = fetchData('/work')
return (
<Suspense fallback={<Spinner />}>
<Work data={work} />
</Suspense>
)
보통 React 에서 data 를 받아올 때 useEffect 안에서 호출한다.
위의 코드가 useEffect 를 사용하는 것과 다른점은 useEffect 의 경우 컴포넌트가 렌더링 된 후, 그러니까 화면이 그려진 다음 실행된다 ( class component 의 경우 componentDidMount )
하지만 Suspense 를 사용할 경우 렌더링 전에 이미 네트워크 호출이 존재한다는 것을 React 에게 알려주게 되며
Data 를 받아오기 전까지 Suspense 의 속성인 fallback 에 할당한 값이 무었이든 그 값을 렌더링한다.
추가로
여러 Data 를 불러오는 코드가 있다고 가정하자
function UserPost() {
const [user, dispUser] = useState(null);
useEffect(() => {
fetchUser().then(u => dispUser(u));
}, []);
if (user === null) {
return <p>Loading information</p>;
}
return (
<>
<p>{user.name}</p>
<UserTimeline />
</>
);
}
function UserTimeline() {
const [posts, setPosts] = useState(null);
useEffect(() => {
fetchPosts().then(p => setPosts(p));
}, []);
if (posts === null) {
return <h2>Loading information</h2>;
}
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.text}</li>
))}
</ul>
);
}
위 코드의 경우 실행순서는
- fetchUser() 로 userData 를 가져온다
- 가져오길 기다린다.
- userData 가져오기를 마친다.
- fetchPost() 로 postData 를 가져온다
- 가져오길 기다린다.
- 가져오길 마친다.
순으로 실행된다.
만약 2 번에서 2초가 걸린다면 5번이 실행되기 까지는 최소 2초의 시간이 지난 후에 실행된다.
병렬구조로 데이터를 fetching 해오는 것이 아니라 순차적으로 실행된다.
물론 ES6 문법의 Promise.all 문법을 통해 여러개의 비동기 통신을 병렬적으로 불러오는 것도 가능하다
function fetchDetails() {
return Promise.all([fetchUserDetails(), fetchuserProfile()])
.then(([userDetails, userProfile]) => ({ userDetails, userProfile }))
}
위의 경우 렌더링 전에 데이터를 먼저 받아올 수 있고 두개의 데이터를 동시에 받을 수 있지만
두개의 요청이 모두 완료된 후에 Data 를 받아올 수 있다.
React 의 Suspense 를 이용하면 위와같은 문제들을 모두 해결할 수 있다.
이 경우 네트워크 요청이 생성됨과 동시에 렌더링을 시작한다.
const resource = getData();
function UserProfile() {
return (
<Suspense fallback={<p>Loading data</p>}>
<UserDetails />
<Suspense fallback={<p>Loading data</p>}>
<ProfileTimeline />
</Suspense>
</Suspense>
);
}
function UserDetails() {
const user = resource.user.read();
return <h1>{user.name}</h1>;
}
function ProfileTimeline() {
const posts = resource. posts.read();
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.text}</li>
))}
</ul>
);
}
위 코드의 경우
첫번째로 UserProfile 을 렌더링한다.
두번째로 UserDetail 을 읽게되지만 data 가 아직 fetch 되지 않은 상태이므로 이 잠시 중단하고 다른 컴포넌트의 렌더링을 시작한다
세번째로 ProfileTimeline 을 읽게되며 두번째와 같은 과정이 발생한다.
네번째로 더이상 렌더링할 다른 컴포넌트가 없다면 React 는 fallback 의 값을 렌더링 하게된다.
Data fetching 이 완료되면 완료되는 순서대로 component 가 렌더링 된다.
하지만 이와같이 View를 비동기로 가져올때 Data 의 도착 순서대로만 보여진다면 레이아웃이 깨질 수 있는 위험이 있다.
Data fetching 순서와 상관없이 코드 작성 순서대로 component 의 렌더링을 하고싶다면
<SuspenseList revealOrder="forwards">
<Suspense fallback={<p>Loading Information </p>}>
<UserWelcome />
<Suspense fallback={<p>Loading Work</p>}>
<Work />
</Suspense>
</Suspense>
</SuspenseList>
SuspenseList 로 Suspense 를 그룹화 한 후 revealOrder 속성에 forwards 값을 지정해주면 가능하다.
revealOrder : option ( 그려질 순서 )
- {undefined}: (default) 데이터가 도착하는 순서대로
- "forwards": 앞에 있는 컴포넌트가 suspending이 끝나야 다음 것을 보여줌 (앞->뒤)
- "backwards": 뒤에 있는 컴포넌트가 suspending이 끝나야 다음 것을 보여줌 (뒤->앞)
- "together": 한꺼번에 보여준다.
주의사항은 SuspensList의 revealOrder효과는 직계 자식에게만 전달된다.
https://www.scaler.com/topics/react/react-suspense/
React Suspense for Fetching Data - Scaler Topics
In this blog by Scaler Topics, we will learn about how you can use React Suspense for Fetching Data with examples and explanations. Read to know more.
www.scaler.com
https://17.reactjs.org/docs/concurrent-mode-reference.html#suspenselist
Concurrent Mode API Reference (Experimental) – React
A JavaScript library for building user interfaces
17.reactjs.org
'react' 카테고리의 다른 글
| React Quill toolbar 관련 오류 해결 ( position : sticky, Clipboard ( 복사 붙여넣기 ) 관련 오류 ) (0) | 2024.05.10 |
|---|---|
| React word book app (0) | 2023.01.02 |