Closed5

memo @240203*

nakamotonakamoto

React 19 hooks

https://marmelab.com/blog/2024/01/23/react-19-new-hooks.html#form-actions

use(Promise)

useの中に非同期処理などの非同期な関数を入れることによってその解決まで行ってくれる。
そもそもuseEffectを使ってフェッチするというのはバッドプラクティスというか
本来やっていいけれどもuseEffectを使って適切に非同期処理を行うのはとても難しい。
useがあったらuseEffectを使ってそれを行うよりもずっと簡単にできる。

import { use } from 'react';

function MessageComponent({ messagePromise }) {
    const message = use(messagePromise);
    // ...
}

従来はRSCならasync awaitを使ってデータをフェッチできたけれども、
クライアントコンポーネントだと基本的にasyncコンポーネントにすることはできず、
useEffectとかなにかしらの方法を使ってフェッチしないといけなかった。
でもuseが出てきたおかげでちょっとしたデータのフェッチとかなら、
TanStack Queryとかも使わなくてよくなりそう。

nakamotonakamoto

use(Context)

Contextuseの中に入れることによってContextの値を読み取れる。
これはuseContextとまったく同じ。しかしループやifなどの条件文内で呼び出せる。
useContextは使わないようになる。

import { use } from 'react';

function HorizontalRule({ show }) {
    if (show) {
        const theme = use(ThemeContext);
        return <hr className={theme} />;
    }
    return false;
}
nakamotonakamoto

Form Actions

formactionformActionと入れることによってformDataを直接取得できるようになる。

app.tsx
import { useState } from 'react';

const AddToCartForm = ({ id, title, addToCart }) => {
  const formAction = async (formData) => {
    try {
      await addToCart(formData, title);
    } catch (e) {
      // show error notification
    }
  };

  return (
    <form action={formAction}>
      <h2>{title}</h2>
      <input type="hidden" name="itemID" value={id} />
      <button type="submit">Add to Cart</button>
    </form>
  );
};

type Item = {
  id: string;
  title: string;
};

const Cart = ({ cart }: { cart: Item[] }) => {
  if (cart.length == 0) {
    return null;
  }
  return (
    <>
      Cart content:
      <ul>
        {cart.map((item, index) => (
          <li key={index}>{item.title}</li>
        ))}
      </ul>
      <hr />
    </>
  );
};

export const App = () => {
  const [cart, setCart] = useState<Item[]>([]);

  const addToCart = async (formData: FormData, title) => {
    const id = String(formData.get('itemID'));
    // simulate an AJAX call
    await new Promise((resolve) => setTimeout(resolve, 1000));
    setCart((cart: Item[]) => [...cart, { id, title }]);

    return { id };
  };

  return (
    <>
      <Cart cart={cart} />
      <AddToCartForm
        id="1"
        title="JavaScript: The Definitive Guide"
        addToCart={addToCart}
      />
      <AddToCartForm
        id="2"
        title="JavaScript: The Good Parts"
        addToCart={addToCart}
      />
    </>
  );
};
nakamotonakamoto

useFormState

useFormStateによってformの戻り値を取得できる。

import { useFormState } from 'react-dom';
import { action } from './action';

function MyComponent() {
    const [state, formAction] = useFormState(action, null);
    // ...
    return <form action={formAction}>{/* ... */}</form>;
}

useFormStatus

useFormStatusによってformがいまどういう状態なのかを取得できる。

const { pending, data, method, action } = useFormStatus();
nakamotonakamoto

useOptimistic

useOptimisticを使うことによってactionの送信中にUIを楽観的に更新できる。

import { useOptimistic } from 'react';

function AppContainer() {
    const [optimisticState, addOptimistic] = useOptimistic(
        state,
        // updateFn
        (currentState, optimisticValue) => {
            // merge and return new state
            // with optimistic value
        },
    );
}

Bonus: Async Transitions

ReactTransition APIを使うことによってUI をブロックせずに状態を更新できる。
たとえば、ユーザーの気が変わったら、以前の状態の変更をキャンセルできる。

function TabContainer() {
    const [isPending, startTransition] = useTransition();
    const [tab, setTab] = useState('about');

    function selectTab(nextTab) {
        // instead of setTab(nextTab), put the state change in a transition
        startTransition(() => {
            setTab(nextTab);
        });
    }
    // ...
}
このスクラップは2024/02/03にクローズされました