* 여러 출처를 사용하였으며, 하단에 출처를 첨부합니다.
Form 제출 -> API 요청 -> 응답 처리 -> UI 반영
기존의 리액트에서는 대기상태 pending states, 오류 errors, 낙관적업데이트 optimistic updates, 연속요청 sequential requests 를 수동으로 처리하였다.
예를 들어서 이런 코드가 있다고 쳐보자.
function UpdateName(){
const [name, setName] = useState('');
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const handleSubmit = async() => {
setIsPending(true);
const error = await updateName(name);
setIsPending(false);
if(error){
setError(error);
return;
}
redirect('/path');
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value) } />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}
여기서 눈여겨 볼거는 이름을 업데이트 할때 ( updateName ) , 이 api에 요청을 하고 응답을 받을 때까지
사용자에게 로딩 중인 것을 보여주기 위해서 뭔가 UI적인 처리를 한다는 것이다.
로딩 중이냐 아니냐를 사용자에게 보여주기 위해서는 화면을 업데이트해야하고, 이 때문에 isPending이라는
state를 사용하게 되었다. 그치만 다 좋은데 setIsPending()이라는 useState 훅을 매번 쓰는 것이 번거로울 수 있고
뽀다구가 나지 않는다.
그래서 useTransition 이 나왔다. ( 사실 굳이 필요 없지만 코드는 깔끔해질듯 )
function UpdateName(){
const [name, setName] = useState('');
const [error, setError] = useState(null);
const [isPending, startPending] = useTransition();
const handleSubmit = async() => {
startPending(async()=>{
const error = await updateName(name);
if(error){
setError(error);
return;
}
redirect('/path');
});
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value) } />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}
비동기 트랜지션은 isPending 상태를 즉시 true로 설정하고, 비동기 요청을 처리하고 isPending을 false로 전환한다.
useTransition을 조금 더 살펴보자.
useTransition
- 컴포넌트의 최상위 수준에서 useTransition을 호출하여 일부 state 업데이트를 transition으로 표시한다.
- useTransition은 어떤 매개변수도 받지 않는다.
- 이 훅은 두개의 항목이 있는 배열을 반환하는데
- isPending 플래그는 대기중인 Transition이 있는지 알려준다.
- startTransition 함수는 상태 업데이트를 Transition으로 표시할 수 있게 해주는 함수이다.
- transition 업데이트는 텍스트 입력을 제어하는데 사용할 수 없다.
- 진행중인 transition이 여러개 있으면, 리애트는 현재 transition을 함께 일괄 처리한다.
startTransition 함수
- useTransition이 반환하는 startTransition 함수를 사용하면 state 업데이트를 Transition으로 표시할 수 있다.
- 매개변수로는 함수가 들어간다. 어떤 함수? scope
- 하나 이상의 set함수를 호출하여 일부 state를 업데이트하는 함수이다.
- React 는 매개변수 없이 scope를 즉시 호출하고 scope 함수를 호출하는 동안 동기적으로 예약된 모든 state 업데이트를 Transition으로 표시한다. 이것은 non-blocking 이며 원치 않는 로딩을 표시하지 않는다.
- startTransition은 아무것도 반환하지 않는다.
- startTransition에 전달하는 함수는 동기식이어야 한다.
React의 startTransition 함수는 상태 업데이트를 전환 Transition으로 표시하여 UI의 반응성을 향상시키는데에 주로 사용된다. Transition으로 표시된 업데이트는 긴급하지 않은 작업으로 간주되어, 사용자 인터페이스가 이러한 업데이트로 인해 차단되지 않도록 한다.
Transition을 통한 상태 업데이트와 일반 업데이트의 차이점
- 일반적인 상태 업데이트는 즉시 처리된다. 하지만, 이로 인해서 복잡한 연산이나 렌더링이 발생하면 UI가 일시적으로 멈추는 현상이 발생할 수 있다.
- 반면에, startTransition으로 감싼 상태 업데이트는 우선 순위가 낮은 작업으로 처리되어서, 사용자 입력과 같은 긴급한 작업이 우선적으로 처리된다. 그래서 UI의 반응성이 유지되고, 사용자는 인터페이스가 멈추지 않는 경험을 하게 된다.
그렇다면 Transition 사용 시에 UI가 블로킹 되지 않는 이유를 알아보자.
- 리액트 18에서는 동시성 렌더링을 도입하여, 여러 작업의 우선 순위를 관리할 수 있게 되었다.
- startTransition을 사용하면 특정 상태 업데이트를 낮은 우선순위의 Transition으로 표시하게 되며, React는 이러한 Transition업데이트를 백그라운들에서 처리한다. 이 과정에서 사용자 입력과 같은 높은 우선순위의 작업이 발생하면, React는 현재 진행중이 Transition 렌더링을 일시 중단하고, 긴급한 작업을 먼저 처리한다. 그 후에 중단된 Transition 작업을 재개하여 처리한다.
- 이렇게 사용자 입력이나 Hover 와 같은 긴급한 렌더링을 먼저 처리하고 Transition의 업데이트를 우선순위가 낮을 걸로 처리해서 후순위로 처리하기 때문에 사용자의 UI 상호작용을 블로킹하지 않을 수 있다.
1. Transition에서 탭을 업데이트 하는 경우
import { useState, useTransition } from 'react';
import TabButton from './TabButton.js';
import AboutTab from './AboutTab.js';
import PostsTab from './PostsTab.js';
import ContactTab from './ContactTab.js';
export default function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
return (
<>
<TabButton
isActive={tab === 'about'}
onClick={() => selectTab('about')}
>
About
</TabButton>
<TabButton
isActive={tab === 'posts'}
onClick={() => selectTab('posts')}
>
Posts (slow)
</TabButton>
<TabButton
isActive={tab === 'contact'}
onClick={() => selectTab('contact')}
>
Contact
</TabButton>
<hr />
{tab === 'about' && <AboutTab />}
{tab === 'posts' && <PostsTab />}
{tab === 'contact' && <ContactTab />}
</>
);
}
2. Transition없이 탭을 업데이트 하는 경우
import { useState } from 'react';
import TabButton from './TabButton.js';
import AboutTab from './AboutTab.js';
import PostsTab from './PostsTab.js';
import ContactTab from './ContactTab.js';
export default function TabContainer() {
const [tab, setTab] = useState('about');
function selectTab(nextTab) {
setTab(nextTab);
}
return (
<>
<TabButton
isActive={tab === 'about'}
onClick={() => selectTab('about')}
>
About
</TabButton>
<TabButton
isActive={tab === 'posts'}
onClick={() => selectTab('posts')}
>
Posts (slow)
</TabButton>
<TabButton
isActive={tab === 'contact'}
onClick={() => selectTab('contact')}
>
Contact
</TabButton>
<hr />
{tab === 'about' && <AboutTab />}
{tab === 'posts' && <PostsTab />}
{tab === 'contact' && <ContactTab />}
</>
);
}
* 만약에 Transition 중에 보류 중인 시각적 state를 표시하려면 다음과 같다.
useTransition이 반환하는 isPending은 boolean 이다.
때문에 그냥 컴포넌트 안에서
if(isPending){
return 돔
}
이런식으로 한다.
그러면 여기서 isPending 일때 state가 잘 업데이트 되었을 때 보여줄 내용을 미리 보여줄 수 있다.
이를 '낙천적 업데이트'라고 하기도 한다.
// 공식 문서의 코드입니다.
import { Suspense, useState } from 'react';
import TabButton from './TabButton.js';
import AboutTab from './AboutTab.js';
import PostsTab from './PostsTab.js';
import ContactTab from './ContactTab.js';
export default function TabContainer(){
const [tab, setTab] = useState('about');
return(
<Suspense fallback={<h1>Loading...</h1>}>
<TabButton
isActive={tab==='about'}
onClick={()=>{setTab('about');}}
>
About
</TabButton>
<TabButton
isActive={tab==='posts'}
onClick={()=>{setTab('posts');}}
>
Posts
</TabButton>
<TabButton
isActive={tab==='Contact'}
onClick={()=>{setTab('contact');}}
>
Contact
</TabButton>
<hr/>
{tab === 'about' && <AboutTab/>}
{tab === 'posts' && <PostsTab/>}
{tab === 'contact' && <ContactTab/>}
</Suspense>
);
}
[출처]
1. React 19 출시와 새로운 기능 | 블로그 | 모두의연구소
2. React 19 베타버전 공식 문서
3. OMIN 블로그
'Frontend > React' 카테고리의 다른 글
Suspense와 ErrorBoundary (0) | 2024.12.03 |
---|---|
useEffect와 setState의 배치 처리 (1) | 2024.12.03 |
왜 리액트 배포하고 새로고침하면 404에러가 뜰까? (0) | 2024.10.11 |
코드 스플리팅 (1) | 2024.10.10 |
validateStatus - axios (1) | 2024.09.18 |