- Today
- Total
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- async
- Angular
- error
- 모던 자바스크립트 deep dive
- JavaScript
- git
- 모던 자바스크립트
- 백준
- js
- 백준 실버
- 알고리즘
- 비동기
- es6
- html
- deep dive
- 상태관리
- map
- 에러처리
- C++
- React
- 네트워크
- git error
- Java Script
- get
- 웹
- 프론트엔드
- 자바스크립트
- 이터러블
- 그림으로 배우는 http&network
- http
sharingStorage
React18 new Hooks 본문
useId
접근성 속성에 전달되는 특별한 ID를 만드는 hooks
import { useId } from 'react';
function PasswordField() {
const passwordHintId = useId();
Usage
- 접근성 attributes 를 위한 특별한 ID를 만들기 위해
- 몇개의 관련된 elements 에 대한 ID를 만들기 위해
- 모든 생성된 ID들에 대한 접두사를 지정하기 위해
- 클라이언트와 서버가 같은 접두사 ID를 사용하기 위해
Parameters
useId는 어떠한 parameter도 존재하지 않습니다.
Returns
useId는 컴포넌트 내에 useId 호출과 관련된 특별한 ID string을 반환합니다.
주의 사항
- useIdHook이므로 컴포넌트의 최상위 수준 이나 자체 Hook에서만 호출할 수 있습니다. 루프나 조건문 내에서는 호출할 수 없습니다. 필요한 경우 새 구성 요소를 추출하고 상태를 해당 컴포넌트로 옮깁니다.
- useId 목록에서 키를 생성하는 데 사용해서는 안 됩니다 .
Ex)
<label>
Password:
<input
type="password"
aria-describedby="password-hint"
/>
</label>
<p id="password-hint">
The password should contain at least 18 characters
</p>
위와 같이 ID하드코딩하는 것은 좋은 습관이 아닙니다. 컴포넌트는 한 페이지에서 여러번 렌더링될 수 있지만 ID는 고유해야하므로 useId를 사용할 수 있습니다.
import { useId } from 'react';
function PasswordField() {
const passwordHintId = useId();
return (
<>
<label>
Password:
<input
type="password"
aria-describedby={passwordHintId}
/>
</label>
<p id={passwordHintId}>
The password should contain at least 18 characters
</p>
</>
);
}
- Aria-describedby
스크린리더 사용자에게 특정 요소의 상세 설명을 제공하거나, 기타 복잡하게 그룹화 된 레이아웃을 설명하는 용도로 사용하는 것.
모든 HTML 요소에 사용 가능하며 ARIA-labelledby보다 더 상세한 설명이 제공되어야할 때 사용.
접근성 api로는 aria 나 ally가 있습니다.
여러 ID생성하는 법
옳은 예
import { useId } from 'react';
export default function Form() {
const id = useId();
return (
<form>
<label htmlFor={id + '-firstName'}>First Name:</label>
<input id={id + '-firstName'} type="text" />
<hr />
<label htmlFor={id + '-lastName'}>Last Name:</label>
<input id={id + '-lastName'} type="text" />
<label htmlFor={id + '-nickName'}>Last Name:</label>
<input id={id + '-nickName'} type="text" />
</form>
);
}
틀린 예
import { useId } from 'react';
export default function Form() {
const id = useId();
const id2= useId();
const id3= useId();
return (
<form>
<label htmlFor={id}>First Name:</label>
<input id={id} type="text" />
<hr />
<label htmlFor={id2}>Last Name:</label>
<input id={id2 } type="text" />
<hr />
<label htmlFor={id3}>Nick Name:</label>
<input id={id3} type="text" />
</form>
);
}
위와 같이 복수의 ID가 필요할 땐 여러번 생성하는 것이 아닌 id를 접두사로 사용해서 재사용합니다.
생성된 모든 ID에 대한 공유 접두사 지정
만약 여러분들이 싱글페이지에서 여러 독립적인 app을 렌더하려고 한다면 createRoot또는 hydrateRoot를 부를 때 identifierPrefix 옵션을 호출하면 됩니다.
이렇게하면 모든 식별자가 지정한 고유한 접두사로 시작하기 때문에 서로 다른 두 앱에서 생성된 ID가 충돌하지 않습니다.
//index.js
import { createRoot } from 'react-dom/client';
import App from './App.js';
import './styles.css';
const root1 = createRoot(document.getElementById('root1'), {
identifierPrefix: 'my-first-app-'
});
root1.render(<App />);
const root2 = createRoot(document.getElementById('root2'), {
identifierPrefix: 'my-second-app-'
});
root2.render(<App />);
//index.html
<!DOCTYPE html>
<html>
<head><title>My app</title></head>
<body>
<div id="root1"></div>
<div id="root2"></div>
</body>
</html>
클라이언트와 서버에서 동일한 ID 접두사 사용
동일한 페이지에서 여러 개의 독립적인 React 앱을 렌더링하고 이러한 앱 중 일부가 서버에서 렌더링되는 경우 클라이언트 측에서 hydrateRoot 호출에 전달하는 IdentifierPrefix가 서버 API에 전달하는 IdentifierPrefix와 동일해야합니다.
// Server
import { renderToPipeableStream } from 'react-dom/server';
const { pipe } = renderToPipeableStream(
<App />,
{ identifierPrefix: 'react-app1' }
);
// Client
import { hydrateRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = hydrateRoot(
domNode,
reactNode,
{ identifierPrefix: 'react-app1' }
);
동형 애플리케이션(Isomorphic)에서 useId hooks
여러분들은 id값을 고유하게 만들기 위해 Math.random 이나 uuid 관련 라이브러리를 사용한 경험이 있을텐데 위 두가지 방법으로 id를 사용하면 React에서 CSR과 SSR을 과정에서 id값이 일치하지 않는 문제가 발생합니다. 이러한 id 불일치는 hydration과 접근성이슈를 발생시키는데, 그 이유는 대부분에 접근성 API (accessibility API)는 컴포넌트에 연결된 id에 의존하기 때문입니다.
Isomorphic React app :
서버와 클라이언트 사이드 둘 다에서 동작하는 코드로 구성된 웹 앱입니다.
서버로부터 가져오는 SEO의 유리함과, 복잡한 유저 상호작용을 처리할 수 있는 브라우저의 능력이 결합되었습니다.
useId를 사용할 때
- 고유한 ID생성
- label 및 input과 같은 HTML요소를 연결
useId를 사용하면 안될 떄
- list에 대한 key사용
- CSS 선택자 사용 (useId가 콜론을 포함하는 문자열을 반환하기 때문)
- 불변의 값일 때
useTransition
what?
useTransition은 UI blocking 없이 상태를 업데이트 시켜주는 React Hook입니다.
const [isPending, startTransition] = useTransition()
parameters
useTransition 은 parameters를 가지지 않습니다.
returns
useTransition은 두개의 요소가 담긴 배열을 리턴합니다.
isPending : 보류중인 transition이 존재하는지에 대한 boolean 값 (상태변화가 지연되고 있음을 알리는 값)
startTransition: transition 우선순위를 변경
(startTransition은 상태 변화를 일으키는 콜백함수를 전달받고 해당 콜백함수는 낮은 우선순위로 실행되게 된다.)
startTransition function
startTransition은 사용자에게 state update 를 transition으로써 표시할 수 있습니다.
why?
debounce, throttle 과의 비교
input에 text를 입력하고 그에 해당되는 화면을 출력하는 페이지를 구성할 때로 예를 들어보겠습니다.
debounce
debounce는 일정시간 뒤에 화면 업데이트를 할 수 있습니다.
그러나 입력이 계속된다면 화면업데이트 역시 계속 미뤄질 수 있습니다.
즉 단순히 문제를 뒤로 미루는 것일 뿐입니다.
throttle
throttling을 이용하여 3초주기로 화면을 그리게 할 수도 있지만 3초동안 유저가 입력하지 않으면 그 시간이 낭비됩니다. 따라서 이 역시 적합한 방법이 아닙니다.
startTransition
반면 startTransition을 사용하면 사용자 입력중에도 화면 업데이트를 계속할 수 있습니다.
위 두개랑 비교해서 비어있던 시간들이 사라지고 사용자 경험을 더 향상시킬 수 있습니다.
How
지연을 미루고 싶은 상태 업데이트 함수를 startTransition으로 감싸줍니다.
startTransition(() => {
setText(e.target.value);
});
isPending을 사용해서 지연이 되고 있다면 loading page나 스피너를 띄워줄 수도 있습니다.
const [isPending, startTransition] = useTransition();
if (isPending) {
return <b className="pending">로딩중 ...</b>;
}
return (
<div> 로딩 끝! </div>
)
다음 예시는 Posts탭에 접근하면 인위적으로 느려지게 render되는 페이지입니다.
게시물을 클릭한 후 바로 contact를 클릭하면 게시물의 렌더링이 중단됩니다. 이 상태 업데이트는 transition으로 감싸져서 느린 리렌더링이 있어도 UI가 멈추거나 기다리지 않습니다.
Suspens & fallback
다음과 같이 Suspens도 지원돼서 loading fallback을 사용할 수도 있습니다
export default function TabContainer() {
const [tab, setTab] = useState('about');
return (
<Suspense fallback={<h1>🌀 Loading...</h1>}>
<TabButton
isActive={tab === 'about'}
onClick={() => setTab('about')}
>
About
</TabButton>
When?
블로킹 렌더 문제를 해결하기 위해
- 한 번 렌더링 연산이 시작되면 멈출 수 없음
- 대현 화면 업데이트의 경우 렌더링 되는 동안 페이지 지연 발생하기 때문에 그 우선순위를 나누고 높은 우선순위 이벤트를 먼저 처리하기 위해
useDeferredValue
What?
UI의 일부 업데이트를 연기할 수 있는 React Hook입니다.
Usage
- 새로운 콘텐츠가 로딩되는 동안 오래된 콘텐츠를 표시할 때
- 콘텐츠가 오래되었음을 나타낼 때
- UI일부에 대해 리렌더링을 연기할 때
import { useState, useDeferredValue } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// ...
}
Parameters
value : 연기하려는 값 : 어떤 타입도 가능합니다.
Returns
초기렌더링에선 반환된 지연값은 사용자가 제공한 값과 동일합니다.
업데이트 하는 동안에 처음엔 old value 를 리턴하고 그 다음엔 new value (update value)를 리턴합니다.
How
import { useDeferredValue, useState } from "react";
export default function Home() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const [count3, setCount3] = useState(0);
const [count4, setCount4] = useState(0);
const deferredValue = useDeferredValue(count2);
const onIncrease = () => {
setCount1(count1 + 1);
setCount2(count2 + 1);
setCount3(count3 + 1);
setCount4(count4 + 1);
};
console.log({ count1 });
console.log({ count2 });
console.log({ count3 });
console.log({ count4 });
console.log({ deferredValue });
위 예시와 같이 가장 낮은 순위로 상태를 변경하고 싶을 때 useDeferredValue로 값을 감싸주면 된다.
새로운 콘텐츠가 로드되는 동안 오래된 콘텐츠 표시
useDeferredValue를 사용하면 React는 지연된 값을 업데이트하지 않고 old value로 리렌더링한 후 백그라운드에서 새로 수신된 값(new value)으로 리렌더링을 시도합니다. 즉 loading중일때 이전 값이 표시됩니다.
when?
useTransition vs useDeferredValue
useTransition은 set함수를 callback으로 받아서 사용한다.
startTransition(() => {
setText(e.target.value);
});
useDeffered는 값을 래핑해서 사용한다.
const deferredValue = useDeferredValue(value);
두개의 hook은 결과적으로 동일한 목표를 달성하므로 두 가지를 함께 사용할 필요는 없습니다.
대체적으로 useTransition을 선호하는 것 같지만 만약 set함수(state update)에 대한 접근권한이 없다면 useDeferredValue를 사용해야합니다.
'Front-End > React' 카테고리의 다른 글
React custom hooks : what, how, when, why (5) | 2023.11.16 |
---|---|
React query와 Asyncronous Recoil (feat Suspense, Loadable) (8) | 2023.05.08 |
Create React App환경에서 이미지 불러오는 방법(public vs src) (0) | 2023.01.25 |
React API 연동하기 2. useReducer로 요청상태 관리하기 (2) | 2023.01.11 |
React API 연동하기 1. React API 사용해보기 (0) | 2023.01.11 |