백엔드 개발 공부, Django로 ‘4개월’이면 가능해요
#소프트웨어 

React 19 출시와 새로운 기능

React19 버전이 출시 되었습니다. 업데이트 된 새로운 기능에 대하여 간략하게 설명하고자 합니다.

2024-05-02 | 김성혁

React 19 베타 릴리스

2024 4 25 React 19 베타 버전 npm 를 통하여 사용할 수 있게 되었습니다. 이 글에서는 React19 의 새로운 기능에 대하여 간략하게 설명하고자 합니다. React 공식 블로그의 글을 번역한 내용입니다.

React 19 새로운 기능

Actions

React에서 일반적으로 데이터를 변형한 다음 응답으로 상태 업데이트하는 방법을 사용합니다. 예를 들어 사용자가 자신의 이름을 변경하기 위하여 Form Data 제출하면 API 요청 처리를 다음 응답을 처리합니다. 이전 버전에서는 pending states(대기 상태), errors(오류), optimistic updates(낙관적 업데이트), sequential requests(연속 요청) 수동으로 처리하였습니다.

예를 들어, pending(대기), error(에러) 상태를 useState 에서 처리할 있었습니다:

// Before Actions
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>
  );
}

React 19 에서는 트랜지션에서 비동기 함수를 사용하여 pending states(대기 상태), errors(오류), optimistic updates(낙관적 업데이트), sequential requests(연속 요청) 를 자동으로 처리할 수 있습니다.

예를 들어, useTransition 사용하여 pending state 처리 있습니다.

// Using pending state from Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = async () => {
    startTransition(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로 전환합니다. 이렇게 하면 데이터가 변경되는 동안 현재 UI를 반응형 및 대화형으로 유지할 수 있습니다.

Actions 기반으로 구축된 React 19 optimistic updates(낙관적인 업데이트) 관리하기 위한 useOptimistic Actions 일반적인 경우를 처리하기 위한 새로운 후크 React.useActionState 도입했습니다. react-dom에서는 Form 자동으로 관리하기 위해 <form> Action 추가하고 폼에서 일반적인 경우의 action 지원하기 위해 useFormStatus 추가하고 있습니다.

React 19에서는 위의 예제를 다음과 같이 단순화할 수 있습니다.

// Using <form> Actions and useActionState
function ChangeName({ name, setName }) {
  const [error, submitAction, isPending] = useActionState(
    async (previousState, formData) => {
      const error = await updateName(formData.get("name"));
      if (error) {
        return error;
      }
      redirect("/path");
    }
  );

  return (
    <form action={submitAction}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>Update</button>
      {error && <p>{error}</p>}
    </form>
  );
}

새로운 hook : useActionState

액션의 일반적인 경우를 더 쉽게 처리할 수 있도록 useActionState라는 새로운 훅을 추가했습니다:

const [error, submitAction, isPending] = useActionState(async (previousState, newName) => {
  const error = await updateName(newName);
  if (error) {
    // You can return any result of the action.
    // Here, we return only the error.
    return error;
  }
  
  // handle success
});

useActionState는 Action을 하는 function를 인자로 받고, 래핑된 Action을 반환합니다. 래핑된 Action이 호출되면 useActionState는 Action의 마지막 결과를 데이터로 반환하고, 보류 중인 상태(pending state) 라면 보류 중(pending)으로 반환합니다.

React DOM: <form> Actions

Actions은 React 19의 새로운 <form> 기능과도 통합되어 있습니다. Actions로 양식을 자동으로 제출하기 위해 <form>, <input>, <button> 요소의 action 및 formAction 프로퍼티로 함수를 전달하는 기능이 추가되었습니다.

<form action={actionFunction}>

<form> action이 성공하면 React는 제어되지 않은 컴포넌트에 대해 자동으로 form을 재설정합니다. <form>을 수동으로 재설정해야 하는 경우, 새로운 requestFormReset React DOM API를 호출할 수 있습니다.

React DOM: 새로운 hook: useFormStatus

디자인 시스템에서는 컴포넌트까지 드릴다운하지 않고도 컴포넌트가 있는 <form>에 대한 정보에 액세스해야 하는 디자인 컴포넌트를 작성하는 것이 일반적입니다. 이 작업은 context를 통해 수행할 수 있지만, 일반적인 경우를 더 쉽게 만들기 위해 새로운 훅인 useFormStatus를 추가했습니다.

import {useFormStatus} from 'react-dom';

function DesignButton() {
  const {pending} = useFormStatus();
  return <button type="submit" disabled={pending} />
}

useFormStatus 는 form이 context 제공자인 것처럼 상위 <form>의 상태를 읽습니다.

새로운 hook: useOptimistic

데이터 변형을 수행할 때 흔히 사용되는 또 다른 UI 패턴은 비동기 요청이 진행되는 동안 최종 상태를 낙관적(optimistically)으로 표시하는 것입니다. React 19에서는 이를 더 쉽게 하기 위해 useOptimistic이라는 새로운 훅을 추가했습니다.

function ChangeName({currentName, onUpdateName}) {
  const [optimisticName, setOptimisticName] = useOptimistic(currentName);

  const submitAction = async formData => {
    const newName = formData.get("name");
    setOptimisticName(newName);
    const updatedName = await updateName(newName);
    onUpdateName(updatedName);
  };

  return (
    <form action={submitAction}>
      <p>Your name is: {optimisticName}</p>
      <p>
        <label>Change Name:</label>
        <input
          type="text"
          name="name"
          disabled={currentName !== optimisticName}
        />
      </p>
    </form>
  );
}

useOptimistic hook은 updateName 요청이 진행되는 동안 optimisticName을 즉시 렌더링합니다. 업데이트가 완료되거나 오류가 발생하면 React는 자동으로 currentName 값으로 다시 전환합니다.

New API: use

React 19에서는 렌더링에서 리소스를 읽을 수 있는 새로운 API를 도입합니다. use
예를 들어, use 중인 프로비저닝을 읽으면 프로비저닝이 해결될 때까지 React가 일시 중단됩니다.

import {use} from 'react';

function Comments({commentsPromise}) {
  // `use` will suspend until the promise resolves.
  const comments = use(commentsPromise);
  return comments.map(comment => <p key={comment.id}>{comment}</p>);
}

function Page({commentsPromise}) {
  // When `use` suspends in Comments,
  // this Suspense boundary will be shown.
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  )
}

또한 use과 함께 컨텍스트를 읽을 수 있으므로 조기 반환 후와 같이 조건부로 컨텍스트를 읽을 수 있습니다.

import {use} from 'react';
import ThemeContext from './ThemeContext'

function Heading({children}) {
  if (children == null) {
    return null;
  }
  
  // This would not work with useContext
  // because of the early return.
  const theme = use(ThemeContext);
  return (
    <h1 style={{color: theme.color}}>
      {children}
    </h1>
  );
}

use API는 후크와 유사하게 렌더링에서만 호출할 수 있습니다. 후크와 달리 use은 조건부로 호출할 수 있습니다. 향후에는 use로 렌더링에서 리소스를 소비하는 더 많은 방법을 지원할 계획입니다.