Closed48

React 19 が出たらしい

ピン留めされたアイテム
hajimismhajimism

Nextのおかげで見慣れてる更新が多い一方で、まだふわっとしかわかってないことも多い。

ただ、16.8 以来の変わりような気がしてる

hajimismhajimism

手元で動かしながらやるか

ということでupgrade guide
https://react.dev/blog/2024/04/25/react-19-upgrade-guide

hajimismhajimism

This beta release is for libraries to prepare for React 19. App developers should upgrade to 18.3.0 and wait for React 19 stable as we work with libraries and make changes based on feedback.

一旦は18.3で我慢しとけよとも書いてある

hajimismhajimism

こういうことだわな

 WARN  Issues with peer dependencies found
.
├─┬ @storybook/addon-essentials 8.0.8
│ └─┬ @storybook/addon-controls 8.0.8
│   └─┬ @storybook/blocks 8.0.8
│     ├── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│     ├── ✕ unmet peer react-dom@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│     └─┬ @storybook/components 8.0.8
│       ├── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│       ├── ✕ unmet peer react-dom@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│       ├─┬ @radix-ui/react-slot 1.0.2
│       │ ├── ✕ unmet peer react@"^16.8 || ^17.0 || ^18.0": found 19.0.0-beta-94eed63c49-20240425
│       │ └─┬ @radix-ui/react-compose-refs 1.0.1
│       │   └── ✕ unmet peer react@"^16.8 || ^17.0 || ^18.0": found 19.0.0-beta-94eed63c49-20240425
│       ├─┬ @storybook/icons 1.2.9
│       │ ├── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│       │ └── ✕ unmet peer react-dom@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│       └─┬ @storybook/theming 8.0.8
│         ├── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│         └── ✕ unmet peer react-dom@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
├─┬ @storybook/addon-links 8.0.8
│ └── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
├─┬ @storybook/nextjs 8.0.8
│ ├── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│ ├── ✕ unmet peer react-dom@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│ └─┬ @storybook/preset-react-webpack 8.0.8
│   ├── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│   ├── ✕ unmet peer react-dom@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│   └─┬ @storybook/react 8.0.8
│     ├── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│     ├── ✕ unmet peer react-dom@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│     ├─┬ @storybook/react-dom-shim 8.0.8
│     │ ├── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│     │ └── ✕ unmet peer react-dom@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│     └─┬ react-element-to-jsx-string 15.0.0
│       ├── ✕ unmet peer react@"^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│       └── ✕ unmet peer react-dom@"^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
├─┬ @testing-library/react 15.0.2
│ ├── ✕ unmet peer react@^18.0.0: found 19.0.0-beta-94eed63c49-20240425
│ └── ✕ unmet peer react-dom@^18.0.0: found 19.0.0-beta-94eed63c49-20240425
├─┬ @mantine/core 7.8.0
│ ├── ✕ unmet peer react@^18.2.0: found 19.0.0-beta-94eed63c49-20240425
│ ├── ✕ unmet peer react-dom@^18.2.0: found 19.0.0-beta-94eed63c49-20240425
│ ├─┬ react-number-format 5.3.4
│ │ ├── ✕ unmet peer react@"^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│ │ └── ✕ unmet peer react-dom@"^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│ ├─┬ react-remove-scroll 2.5.9
│ │ ├── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│ │ ├─┬ react-remove-scroll-bar 2.3.6
│ │ │ ├── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│ │ │ └─┬ react-style-singleton 2.2.1
│ │ │   └── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│ │ ├─┬ use-callback-ref 1.3.2
│ │ │ └── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│ │ └─┬ use-sidecar 1.1.2
│ │   └── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│ └─┬ react-textarea-autosize 8.5.3
│   ├── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│   ├─┬ use-composed-ref 1.3.0
│   │ └── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│   └─┬ use-latest 1.2.1
│     ├── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
│     └─┬ use-isomorphic-layout-effect 1.1.2
│       └── ✕ unmet peer react@"^16.8.0 || ^17.0.0 || ^18.0.0": found 19.0.0-beta-94eed63c49-20240425
├─┬ next 14.2.1
│ ├── ✕ unmet peer react@^18.2.0: found 19.0.0-beta-94eed63c49-20240425
│ └── ✕ unmet peer react-dom@^18.2.0: found 19.0.0-beta-94eed63c49-20240425
└─┬ @mantine/hooks 7.8.0
  └── ✕ unmet peer react@^18.2.0: found 19.0.0-beta-94eed63c49-20240425
hajimismhajimism

Errors in render are not re-thrown

初っ端からピンときてない

hajimismhajimism

Removed deprecated React APIs

Removed deprecated React DOM APIs

知らんやつか、知ってても普段触らんやつばっかやった

hajimismhajimism

When double rendering in Strict Mode in development, useMemo and useCallback will reuse the memoized results from the first render during the second render. Components that are already Strict Mode compatible should not notice a difference in behavior.

Strict Mode 微修正

hajimismhajimism

ref cleanups required

うーんピンときてないかも
refがよくわかってない説

hajimismhajimism

A long-time complaint of how TypeScript and React work has been useRef. We’ve changed the types so that useRef now requires an argument. This significantly simplifies its type signature. It’ll now behave more like createContext.

This now also means that all refs are mutable. You’ll no longer hit the issue where you can’t mutate a ref because you initialised it with null:

useRef still has a convenience overload for useRef<T>(null) that automatically returns RefObject<T | null>. To ease migration due to the required argument for useRef, a convenience overload for useRef(undefined) was added that automatically returns RefObject<T | undefined>.

これは、はい、あざす

hajimismhajimism

A long-time request is to remove the global JSX namespace from our types in favor of React.JSX. This helps prevent pollution of global types which prevents conflicts between different UI libraries that leverage JSX.

そういうこともあるか

hajimismhajimism

Better useReducer typings

useReducer<React.Reducer<State, Action>>(reducer) って書いたことないからピンと来てないな?相変わらず const reducer = (state: State, action: Action) => state;でいいのよね?

hajimismhajimism

What’s new in React 19

hajimismhajimism

In React 19, we’re adding support for using async functions in transitions to handle pending states, errors, forms, and optimistic updates automatically.

The async transition will immediately set the isPending state to true, make the async request(s), and switch isPending to false after any transitions. This allows you to keep the current UI responsive and interactive while the data is changing.

transitionがasyncを受け付けるようになり、直観的なwaiting表現ができるようになった

  const handleSubmit = async () => {
    startTransition(async () => {
      const error = await updateName(name);
      if (error) {
        setError(error);
        return;
      } 
      redirect("/path");
    })
  };
hajimismhajimism

By convention, functions that use async transitions are called “Actions”.

らしい。

hajimismhajimism

<form> elements now support passing functions to the action and formAction props.

も気になる

hajimismhajimism

おそらく今回の大トロ

  const [error, submitAction, isPending] = useActionState(
    async (previousState, formData) => {
      const error = await updateName(formData.get("name"));
      if (error) {
        return error;
      }
      redirect("/path");
    }
  );
hajimismhajimism

拾ってきた

    export function useActionState<State>(
        action: (state: Awaited<State>) => State | Promise<State>,
        initialState: Awaited<State>,
        permalink?: string,
    ): [state: Awaited<State>, dispatch: () => void, isPending: boolean];
    export function useActionState<State, Payload>(
        action: (state: Awaited<State>, payload: Payload) => State | Promise<State>,
        initialState: Awaited<State>,
        permalink?: string,
    ): [state: Awaited<State>, dispatch: (payload: Payload) => void, isPending: boolean];

hajimismhajimism

React.useActionState was previously called ReactDOM.useFormState in the Canary releases, but we’ve renamed it and deprecated useFormState.

useFormState使ってたところを改修しなきゃだ

hajimismhajimism

型としては State | Promise<State>だけど、Successの場合はaction内で処理書けちゃうからerrorだけ返してるのか。なんかいびつな気もする?

  if (error) {
    // You can return any result of the action.
    // Here, we return only the error.
    return error;
  }
  
  // handle success
hajimismhajimism

なんか、パッと見Result型のStateを返すと良いかなと思ったけど、そうでもないかな

hajimismhajimism

optional permalink: A string containing the unique page URL that this form modifies. For use on pages with dynamic content (eg: feeds) in conjunction with progressive enhancement: if fn is a server action and the form is submitted before the JavaScript bundle loads, the browser will navigate to the specified permalink URL, rather than the current page’s URL. Ensure that the same form component is rendered on the destination page (including the same action fn and permalink) so that React knows how to pass the state through. Once the form has been hydrated, this parameter has no effect.

わかりそうで一部わからんかも

hajimismhajimism

うーん、initialStateが必須になってたり、isPendingが返ってきてなかったりと、噛み合わないところがあるな?

hajimismhajimism

New hook: useOptimistic

これなー、ちょっとむずいよね
本丸のStateは親のもので、propsで受け取っといて、optimistic state は子供でもって、表示だけ上書きする。

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>
  );
}
hajimismhajimism

useねー
これもちょっとgame変わるのかなー
キャッシュ周りのこと考えると結局tanstackなどのライブラリを使うことになるのかなー

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

use does not support promises created in render.

んー、これもふわっとしかわかってないな

hajimismhajimism

In the future we plan to support more ways to consume resources in render with use.

useはまだまだ発展途上って感じかなー
自分の理解度も浅いからもうちょい様子見かも

hajimismhajimism

This separate environment is the “server” in React Server Components. Server Components can run once at build time on your CI server, or they can be run for each request using a web server.

ちょっと前に物議を醸したやつだw

hajimismhajimism

ref as a prop

PropsWithRef みたいな形が提供されてるかなと思ったら見つからなかった

hajimismhajimism

てかProviderにもConsumerにもなるContextオブジェクトどんな実装やろか
useで何が取り出されているんやろ

hajimismhajimism

extends Providerらしい

    interface Context<T> extends Provider<T> {
        Provider: Provider<T>;
        Consumer: Consumer<T>;
        /**
         * Used in debugging messages. You might want to set it
         * explicitly if you want to display a different name for
         * debugging purposes.
         *
         * @see {@link https://legacy.reactjs.org/docs/react-component.html#displayname Legacy React Docs}
         */
        displayName?: string | undefined;
    }
hajimismhajimism

useはこう。単にContextが来たらContext.Consumer取り出すようにしているだけか

   export type Usable<T> = Thenable<T> | Context<T>;

    export function use<T>(usable: Usable<T>): T;
hajimismhajimism

Previously, React would call ref functions with null when unmounting the component. If your ref returns a cleanup function, React will now skip this step.

In future versions, we will deprecate calling refs with null when unmounting components.

ref周りちょっと注意かもな
次使う場面あったらちゃんとドキュメント読もう

hajimismhajimism

Support for Document Metadata

おおっ?これすごい変化だな
便利やけどキモいって言われそう
あと挿入順も気になるな

hajimismhajimism

Support for stylesheets

これちょっとおもろいな。Suspenseがここにつながるとは思ってなかった。

hajimismhajimism

In React 19 we’ve included better support for async scripts by allowing you to render them anywhere in your component tree, inside the components that actually depend on the script, without having to manage relocating and deduplicating script instances.

すごいな、えらいね

hajimismhajimism

Support for preloading resources

最適化の手法がひとつ増えたねー

hajimismhajimism

Compatibility with third-party scripts and extensions

悩み減りそう

このスクラップは4ヶ月前にクローズされました