react.dev のご紹介

March 16, 2023 by Dan Abramov and Rachel Nabors


本日、React とそのドキュメントの新しいホームとなる react.dev の立ち上げを発表することができ、大変うれしく思います。この記事では、新しいサイトの見どころをご紹介します。


tl;dr

  • 新しい React サイト (react.dev) では、関数コンポーネントとフックを用いて、モダンな React を学べます。
  • 図解、イラスト、チャレンジ問題、そして 600 以上の新しいインタラクティブなサンプルが含まれています。
  • これまでの React ドキュメントサイトは、legacy.reactjs.org に移転しました。

新しいサイト、新しいドメイン、新しいホームページ

まずは少々事務的なところから。

新しいドキュメントの立ち上げを祝うために、そして何よりも、古いコンテンツと新しいコンテンツを明確に分離するために、より短い react.dev ドメインに移行しました。古い reactjs.org ドメインは、こちらのサイトにリダイレクトされるようになります。

古い React ドキュメントは、legacy.reactjs.org にアーカイブされました。「ウェブを破壊」してしまわないよう、古いコンテンツへの既存のリンクは、すべてそちらへ自動的にリダイレクトされるようになっていますが、レガシーサイトへのアップデートはほぼ行われなくなります。

信じられないかもしれませんが、React はもうすぐ 10 歳になります。JavaScript 時間に換算すれば丸々 1 世紀のようなものです! React のホームページをリフレッシュし、ユーザインターフェースを作成するために React が最適な方法であると私たちが考える理由を反映させました。また、スタートガイドも更新し、現代の React ベースのフレームワークにも目立つように言及しました。

まだ新しいホームページをご覧になっていない方は、ぜひチェックしてみてください!

フックのあるモダン React に全面移行

2018 年に React フック (hook) をリリースした際、フックのドキュメントはクラスコンポーネントに精通していることを前提としていました。これにより、コミュニティは非常に迅速にフックを採用することができましたが、しばらくすると古いドキュメントは新しい読者に対応できなくなりました。新しく React を学ぶ人は、クラスコンポーネントを使った学習とフックを使った学習の 2 回に分けて React を学ばなければなりませんでした。

新しいドキュメントでは、最初からフックを用いて React を学びます。ドキュメントは主に 2 つのセクションに分かれています。

  • React を学ぶ は、ゼロから React を学ぶ自己学習型のコースです。
  • API リファレンス では、すべての React API の詳細と使い方のサンプルが提供されています。

それぞれのセクションで見ることができる内容を詳しく見ていきましょう。

補足

フックが対応していないクラスコンポーネントの稀なユースケースが、まだわずかに存在します。クラスコンポーネントは引き続きサポートされており、新しいサイトの レガシー API セクションにドキュメントがあります。

クイックスタート

学習セクションはクイックスタートページから始まります。これは React の短い紹介ツアーです。コンポーネント、props、state などの概念に対応する構文を紹介しますが、それらの使い方については詳しく説明していません。

実際にやりながら学ぶことが好きな方は、次に三目並べのチュートリアルをチェックしてください。React を使って小さなゲームを作る方法を説明しながら、毎日使うスキルを教えてくれます。以下が実際に作成するものです。

import { useState } from 'react';

function Square({ value, onSquareClick }) {
  return (
    <button className="square" onClick={onSquareClick}>
      {value}
    </button>
  );
}

function Board({ xIsNext, squares, onPlay }) {
  function handleClick(i) {
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[i] = 'X';
    } else {
      nextSquares[i] = 'O';
    }
    onPlay(nextSquares);
  }

  const winner = calculateWinner(squares);
  let status;
  if (winner) {
    status = 'Winner: ' + winner;
  } else {
    status = 'Next player: ' + (xIsNext ? 'X' : 'O');
  }

  return (
    <>
      <div className="status">{status}</div>
      <div className="board-row">
        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />
        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />
        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />
      </div>
      <div className="board-row">
        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />
        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />
        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />
      </div>
      <div className="board-row">
        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />
        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />
        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />
      </div>
    </>
  );
}

export default function Game() {
  const [history, setHistory] = useState([Array(9).fill(null)]);
  const [currentMove, setCurrentMove] = useState(0);
  const xIsNext = currentMove % 2 === 0;
  const currentSquares = history[currentMove];

  function handlePlay(nextSquares) {
    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
    setHistory(nextHistory);
    setCurrentMove(nextHistory.length - 1);
  }

  function jumpTo(nextMove) {
    setCurrentMove(nextMove);
  }

  const moves = history.map((squares, move) => {
    let description;
    if (move > 0) {
      description = 'Go to move #' + move;
    } else {
      description = 'Go to game start';
    }
    return (
      <li key={move}>
        <button onClick={() => jumpTo(move)}>{description}</button>
      </li>
    );
  });

  return (
    <div className="game">
      <div className="game-board">
        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />
      </div>
      <div className="game-info">
        <ol>{moves}</ol>
      </div>
    </div>
  );
}

function calculateWinner(squares) {
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}

また、React の流儀についても特筆したいと思います。これが多くの人にとって React が「ピンとくる」きっかけとなったチュートリアルです。これらの古典的なチュートリアルも、関数コンポーネントとフックを使用するものにアップデートされ、新品同様となっています。

補足

上記のサンプルはサンドボックスとなっています。サイト全体にたくさんのサンドボックスを追加しました。その数 600 個以上です! どのサンドボックスも編集でき、右上隅の “Fork” ボタンを押すと別のタブで開くことができます。サンドボックスでは、React の API をすばやく試すことができ、アイデアの探求や、理解度のチェックができます。

React をステップバイステップで学ぶ

世界中の誰もが、無料で自分のペースで React を学ぶ機会があるようにしたいと考えています。

このため、学習セクションは章に分かれた自己学習型のコースのように構成されています。最初の 2 章では React の基本が説明されています。これから React を始める方、または記憶をリフレッシュしたい方は、こちらから始めてください:

次の 2 つの章はより高度であり、やや難しいトピックについて深い洞察が得られるようになっています:

  • state の管理 では、アプリの複雑性が増すにつれて、ロジックをどのように整理するかを学びます。
  • 避難ハッチ では、React の「外に出る」方法と、どんなときにそれをする意味があるのかについて学びます。

各章はいくつかの関連するページで構成されています。これらのページのほとんどは、特定のスキルやテクニックを教えるためのものです。例えば、JSX でマークアップを書くstate 内のオブジェクトを更新するコンポーネント間で state を共有する、などです。ページの中には、レンダーとコミットスナップショットとしての state のような、概念を説明するものもあります。また、そこに副作用は要らないかもしれない など、これまでの経験から得られた提案を共有するためのページもあります。

これらの章をこの順番で読む必要はありません。そんな時間がどこにあるでしょうか? ですがそうしても構いません。学習セクションのページは、より前の段階で紹介された概念にのみ依存しています。本のように読みたい場合は、順番に読み進めてください!

チャレンジ問題で理解度を確認する

学習セクションのほとんどのページは、理解度を確認するためのいくつかのチャレンジ問題で終わります。例えば、条件付きレンダー に関するページでは、以下のようなチャレンジ問題がいくつか提供されています。

今すぐここで解いてみる必要はありません! ただし本当にやりたい場合はどうぞ。

チャレンジ 1/2:
? : を使って未梱包アイコンを表示

条件演算子 (cond ? a : b) を使って、isPackedtrue でない場合は ❌ をレンダーするようにしてください。

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✅'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

左下の角にある「答えを見る」ボタンに気付きましたか? 自分でチェックしたい場合に便利です。

図解とイラストで直感を養う

コードと言葉だけでは説明が難しかった場合、直感を養うための図解を追加しました。例えば以下は、state の保持とリセット にある図のひとつです。

Diagram with three sections, with an arrow transitioning each section in between. The first section contains a React component labeled 'div' with a single child labeled 'section', which has a single child labeled 'Counter' containing a state bubble labeled 'count' with value 3. The middle section has the same 'div' parent, but the child components have now been deleted, indicated by a yellow 'proof' image. The third section has the same 'div' parent again, now with a new child labeled 'div', highlighted in yellow, also with a new child labeled 'Counter' containing a state bubble labeled 'count' with value 0, all highlighted in yellow.
Diagram with three sections, with an arrow transitioning each section in between. The first section contains a React component labeled 'div' with a single child labeled 'section', which has a single child labeled 'Counter' containing a state bubble labeled 'count' with value 3. The middle section has the same 'div' parent, but the child components have now been deleted, indicated by a yellow 'proof' image. The third section has the same 'div' parent again, now with a new child labeled 'div', highlighted in yellow, also with a new child labeled 'Counter' containing a state bubble labeled 'count' with value 0, all highlighted in yellow.

sectiondiv に変わると、section は削除され、新しい div が追加される

ドキュメントの中にはイラストもいくつかあります。こちらはブラウザが画面を描画しているイラストとなります:

A browser painting 'still life with card element'.

Illustrated by Rachel Lee Nabors

ブラウザベンダに確認したところ、この描写は 100% 科学的に正確であるとのことです。

新しい、詳細な API リファレンス

API リファレンス では、すべての React API に専用のページが用意されています。これにはあらゆる種類の API が含まれます:

  • 組み込みフックである useState など。
  • 組み込みコンポーネントである <Suspense> など。
  • 組み込みブラウザコンポーネントである <input> など。
  • フレームワーク向けの API である renderToPipeableStream など。
  • その他の React API である memo など。

各 API ページは少なくともリファレンス使用法の 2 つのセグメントに分かれていることに気付くでしょう。

リファレンスは、API の引数と返り値をリストアップすることによって、正式な API シグネチャを説明します。簡潔ですが、その API に慣れていない場合は少し抽象的に感じることがあります。API が何をするのかは説明しますが、我々がどのように使用するのかは説明しません。

使用法では、実際にどのようにこの API を使用するのかを、同僚や友人が説明するような形で示します。これは、React チームが各 API を使用するために意図した標準的なシナリオを示しています。色分けされたスニペット、異なる API を一緒に使用するサンプル、コピーペーストできるレシピも追加しました:

Basic useState examples

1/4:
カウンタ(数値)

この例では、count state 変数が数値を保持しています。ボタンをクリックすると、それが増加します。

import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      You pressed me {count} times
    </button>
  );
}

一部の API ページには、(よくある問題に対する)トラブルシューティングや、(非推奨の API に対する)代替手段のセクションも含まれています。

このアプローチにより、API リファレンスが引数を調べる手段としてだけでなく、その API を使用してどれだけ色々なことができるのか、他の API とどのように繋がっているのかを示す手段として役立つことを願っています。

この次は?

これで私たちのミニツアーは終了です! 新しいウェブサイトを見て回り、好きな部分や嫌いな部分を見つけたら、これからもイシュートラッカにフィードバックを送ってください。

このプロジェクトのリリースまでに長い時間がかかってしまったことを認識しています。私たちは React コミュニティに読んでいただくに値する、高い品質のハードルを維持したかったのです。これらのドキュメントを書き、数々のサンプルを作っていくにあたり、私たちは自身のこれまでの説明が誤っていたことに気づき、React のバグを見つけ、さらには React の設計自体の不備を見つけて対処を始めたりすらしました。新しいドキュメントが、今後 React 自体の品質を一段階引き上げるのに役立つことを願っています。

ウェブサイトのコンテンツや機能を拡充するための要望を、既に皆さんから多数いただいています。例えば:

  • すべての例に TypeScript バージョンを提供する
  • パフォーマンス、テスト、アクセシビリティガイドの更新版を作成する
  • React Server Components のドキュメント化を、既にこれをサポートするフレームワークのドキュメントとは独立して行う
  • 国際コミュニティと協力して新しいドキュメントを翻訳する
  • 新サイトに欠けている機能を追加する(例えば、このブログの RSS)

今回 react.dev が無事リリースされたことで、サードパーティの React 学習リソースに「追いつく」という目標から、新しい情報を追加して新しいウェブサイトをさらに改善することへと、焦点を移すことができるようになりました。

React を学ぶには、今がこれまでで最高のタイミングだと思います。

誰がこのプロジェクトに取り組んだのか?

React チームでは、Rachel Nabors がプロジェクトをリードし(イラストも提供)、Dan Abramov がカリキュラムをデザインしました。この 2 人が共同でほとんどのコンテンツを執筆しました。

もちろん、これほど大きなプロジェクトが少人数で進むことはありません。お礼を言いたい方がたくさんいます!

Sylwia Vargas は、“foo/bar/baz” や猫を安易に使ったものではない、世界中の科学者・芸術家・都市をフィーチャーしたサンプルを作成しました。Maggie Appleton は、我々の落書きをクリアな図解にしました。

執筆の協力に感謝します:David McCabeSophie AlpertRick HanlonAndrew ClarkMatt Carroll。アイディアとフィードバックを頂いた方に感謝します:Natalia TepluhinaSebastian Markbåge

サイトデザインに感謝します:Dan Lebowitz。サンドボックスデザインに感謝します:Razvan Gradinar

開発面では、プロトタイプ開発に感謝します:Jared Palmer。UI 開発のサポートに感謝します:Dane GrantDustin Goodman (ThisDotLabs)。サンドボックス統合に感謝します:Ives van HoorneAlex MoldovanJasper De MoorDanilo Woznica (CodeSandbox)。開発やデザインの手直し、色づけや細かい部分の調整に感謝します:Rick Hanlon。サイトに新機能を追加し、メンテナンスしてくれる Harish KumarLuna Ruan に感謝します。

アルファテスト及びベータテストプログラムに参加してくださった皆さんに心から感謝します。皆さんの情熱と貴重なフィードバックによって、このドキュメントを形作ることができました。そして React Conf 2021 で自身の経験をもとに React ドキュメントについて語っていただいた、ベータテスタの Debbie O’Brien に特別な感謝を送ります。

最後に、この取り組みの背後にあるインスピレーションを与えてくださった React コミュニティに感謝します。これを行っているのは皆さんがいるからです。新しいドキュメントが、皆さんの望むどのようなユーザインターフェースであっても、それを React で構築する際の手助けとなれば幸いです。