일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- AppleDeveloperAcademy
- Front-end
- 코드트리
- 코딩테스트
- 코드트리챌린지
- TypeScript
- error
- globalcommunity
- swiftUI
- react-query
- Xcode
- frontend
- JavaScript
- 프로젝트
- 코딩테스트실력진단
- react
- NextJs
- UIKit
- 회고
- velog
- iOSDeveloper
- 자바스크립트
- ios
- 프로그래머스
- tshaped
- 알고리즘
- SWIFT
- git
- 프론트엔드
- Apple Developer Academy
Archives
- Today
- Total
Moon Work
React Suspense 적용기 본문
시작하며
React에서 비동기를 사용할 경우 기본 hook을 제외하고는 아래와 같은 방식으로 컴포넌트가 작성되곤 한다.
import { useEffect, useState } from 'react';
import { User } from '../types/user';
const UsersNoQuery = () => {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<boolean>(false);
useEffect(()=>{
(async ()=> {
try{
const response = await fetch('https://jsonplaceholder.typicode.com/users').then(res=>res.json())
setUsers(response);
}catch(e){
console.log(e);
setError(true);
}finally{
setLoading(false);
}
})();
},[]);
return (
<>
{
loading ?
<h1>loading...</h1>
:
users &&
<ul>{
users.map((user,index)=>{
return <li key={index}> user... </li>
})
}</ul>
}
</>
)
}
export default UsersNoQuery;
query를 사용하면 비동기 부분이 조금 더 단순해지긴 하지만 항상 렌더링 부분에 if문이 들어가는 것에서 더 나은 가독성을 가진 방법이 있지 않을까 하는 의문이 들었다. 또한 비동기 요청을 하는 부분과 조건에 따라 다른 렌더를 하는 부분이 같은 컴포넌트 안에 있는 것이 유연성과 재사용성을 안좋게 만든 다는 생각이 들었다. 여기서 React 18에서 나온 Suspense를 통해 보다 나은 방식으로 바꿀 수 있었다.
React Suspense
- Suspense를 사용하게 되면 REST API나 GraphQL 등 비동기로 데이터를 가져오는 동안 다른 컴포넌트를 보여준다.
- <Suspense>로 컴포넌트를 감싸줄 경우 해당 컴포넌트의 렌더링을 특적 작업(비동기 등) 이후로 미루고 그 작업이 완수될 때까지 falback에 담긴 component를 대신 보여줄 수 있다.
- 과정을 중점에 두는 명령형 프로그래밍이 아닌 결과와 가독성에 중점을 둔 선언형 프로그래밍을 강화한다.
- 컴포넌트가 먼저 렌더된 뒤 나중에 데이터를 받으면서 생기는 waterfall 현상이 데이터를 받은 뒤 렌더 되는 과정을 통해 방지된다.
Suspense 적용하기
Suspense를 적용하게 될 경우 아래와 같이 컴포넌트를 분리해서 page component, container component, item component로 분리할 수 있었다.
import { Suspense, useEffect, useState } from 'react';
import { User } from '../types/user';
const UserListPage = () => {
return (
<Suspense fallback={<h1>Load user...</h1>}>
<UserListContainer />
</Suspense>
)
}
const UserListContainer = () =>{
const [users,setUsers] = useState<User[]>([]);
const fetchUsers = async () =>{
const response = await fetch('https://jsonplaceholder.typicode.com/users')
.then(res=> res.json());
setUsers(response);
}
useEffect(()=>{
fetchUsers();
},[]);
return (
<ul>{
users.map(user=>{
return <UserItem key={user.id} user={user} />
})
}</ul>
)
}
const UserItem = (user:any) =>{
return <li>{user.user.name}</li>
}
export default UserListPage;
Suspense + react-query
react-query를 사용하면 비동기 작업을 보다 가독성 좋게 표현할 수 있다. query의 option에 suspense:true를 통해 suspense에 데이터를 정상적으로 받아온 것을 전달 할 수 있다. 최종적으로 react-query와 suspense를 사용을 통해 보다 component의 재사용과 확장성을 높일 수 있었고 더 나은 가독성을 가진 코드를 작성할 수 있었다.
import { Suspense } from 'react';
import { useQuery } from 'react-query';
import { fetchUsers } from '../api/fetchUser';
import { User } from '../types/user';
const UserListPage = () => {
return (
<Suspense fallback={<h1>Load User...</h1>}>
<UserListContainer />
</Suspense>
)
}
const UserListContainer = () =>{
const {data:users} = useQuery(['productsSuspense'], fetchUsers, {
suspense: true,
})
return (
<ul>{
users.map((user:User)=>{
return <UserItem key={user.id} user={user} />
})
}</ul>
)
}
type UserItemProps = {
user:User
}
const UserItem = ({user}:UserItemProps) =>{
return <li>{user.name}</li>
}
export default UserListPage;
아직 suspense를 완전히 이해한 것은 아니지만 사용하면서 skeleton ui가 필요할 경우나 그 외 활용 방법에 대해 더 공부가 필요해보인다.
'React' 카테고리의 다른 글
알았는데 까먹은 React 기록 - useMemo vs useCallback (0) | 2023.01.05 |
---|---|
알았는데 까먹은 React 기록 - useEffect와 componentWillUnmount (0) | 2022.12.26 |
Custom hook 기초 (0) | 2022.12.08 |
react-query 기초 및 적용하기 (0) | 2022.12.07 |
[사용성 개선] Lazy loading with react, typescript (0) | 2022.12.04 |