Next.js入門のためのReact基礎

### はじめに

Reactの勉強を始めようとする
↓
[公式のチュートリアル](https://react.dev/learn/start-a-new-react-project
)を見に行ったら、「Reactを使うなら、何かフレームワークを使うのがオススメだよ！」と言われる
↓
じゃあNext.jsの勉強するか、ということで[チュートリアル](https://nextjs.org/learn)を見に行く
↓
「Next.js以前に、そもそもReactをよく知らない人は、まずReactの基礎から勉強するのがオススメだよ！」と言われる <- 今ココ


ということで、Next.jsへ入門するためにReact基礎のドキュメントを読みました。その自分なりの要約を書いていきます。

https://nextjs.org/learn/react-foundations

#### 構成

* Chapter 1 〜 3: 前提知識
    * ReactとNext.jsそれぞれの特徴や、必要な前提知識（DOMなど）を学ぶ
* Chapter 4 〜 7: React基礎
    * 簡単なサンプルアプリの構築を通して、ReactによるUI構築の基礎を学ぶ
* Chapter 8 〜 11: Next.jsへの導入
    * ReactのサンプルアプリをNext.jsに置き換え、Next.jsの基礎の基礎を学ぶ


### Chapter 1: React と Next.js について

https://nextjs.org/learn/react-foundations/what-is-react-and-nextjs

#### React

ReactはJavaScriptの**ライブラリ**であり、インタラクティブなUIを提供するためのもの。
ReactはUI構築のための便利な機能を数多く提供するが、それらの機能をアプリケーションのどこで使うかは開発者に委ねられている。そのため**ライブラリ**と呼んでいる。

#### Next.js

Next.jsはWebアプリケーションを構築するためのReact用**フレームワーク**。
Reactを使うために必要なツールや設定が一通り揃っており、さらに便利な機能や構造、アプリケーションの性能を良くするための仕組みも提供する。これらの特徴から**フレームワーク**と呼ばれる。

Reactを使ってUIを構築し、その上でNext.jsの機能を利用することで、多くのアプリケーションに必要な要件を実現できる。

### Chapter 2: UIのレンダリング

https://nextjs.org/learn/react-foundations/rendering-ui

Reactの仕組みを理解するには、DOMについて知る必要がある。

DOMはHTML要素のオブジェクト表現であり、ツリーのような構造を持っている。JavaScriptからDOMを操作することで、特定の要素を増やしたり、レイアウトを変更したりすることができる。

![スクリーンショット 2024-08-15 23.03.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760224/af8d80bc-fa86-efc0-f841-dca92a09b393.png)


### Chapter 3: JavaScriptによるUIの更新

https://nextjs.org/learn/react-foundations/updating-ui-with-javascript

JavaScriptによるDOM操作の例をみていく。

下記のコードは、`app`というidを持つ要素に、子要素として「テキスト付きの`<h1>`要素」を追加するもの。

```html
<script type="text/javascript">
  const app = document.getElementById('app');
  const header = document.createElement('h1');
  const text = 'Develop. Preview. Ship.';
  const headerContent = document.createTextNode(text);
  header.appendChild(headerContent);
  app.appendChild(header);
</script>
```

このようなコードは「命令型プログラミング」と呼ばれるもので、UIがどのように更新されるかの具体的な手順を記載している。

具体的な手順を示すのではなく、表示したいものを宣言するだけで済むのなら、その方が便利。この手法は「宣言型プログラミング」と呼ばれる。UIの構築ではこちらの方が好ましい。

言い換えれば、シェフにピザの作り方を1工程ずつ指示するのではなく、単にピザをくださいと注文するようなもの。

Reactは宣言型のライブラリであり、開発者はUIに対して求めることだけを記載すれば良い。その実現方法はReactが考えてくれる。


### Chapter 4: React入門

https://nextjs.org/learn/react-foundations/getting-started-with-react

Reactを使うには、`react`と`react-dom`という2つのライブラリが必要。
`Chapter 3`のDOM操作をReactで書き換えると、こんなに簡潔になる。

```html: index.html
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script>
  const app = document.getElementById('app');
  const root = ReactDOM.createRoot(app);
  root.render(<h1>Develop. Preview. Ship.</h1>);
</script>
```

ただし、このままでは文法エラーが発生してしまう。

```terminal
Uncaught SyntaxError: expected expression, got '<'
```

原因は、`render`に渡した`<h1>...</h1>`が**JSX**であり、正しいJavascriptではないから。


#### JSXとは？

JSXはJavaScriptの構文を拡張したもので、HTMLのような構文でUIを記述できることが特徴。HTMLとJavaScriptの記法をそのまま使えるのが美点だが、3つのJSXルール[^jsx]には従わなければいけない。

[^jsx]: 3つのJSXルールの詳細はこちら: https://react.dev/learn/writing-markup-with-jsx#the-rules-of-jsx

JSXはJavaScriptを拡張したものであり、JavaScriptとは違うもの。そのため、JSXのままではブラウザが理解できない。先ほどの文法エラーはこれが原因。エラーを解消するには、BabelのようなJavaScriptコンパイラを使って、JSXを通常のJavaScriptに変換する必要がある。

#### プロジェクトにBabelを加える

エラー解消のためには、以下の2点が必要

* `babel.min.js`の読み込みを追加
* `<script>`に種類(type)として`text/jsx`を追記

```html: index.html
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- Babel Script -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/jsx">
  const domNode = document.getElementById('app');
  const root = ReactDOM.createRoot(domNode);
  root.render(<h1>Develop. Preview. Ship.</h1>);
</script>
```

### Chapter 5: コンポーネントによるUI構築

https://nextjs.org/learn/react-foundations/building-ui-with-components

Reactの中核となるコンセプトは3つある。

* Components(コンポーネント）
* Props(プロップス = プロパティの意味）
* State(ステート) 

`Chapter 5`では、Componentsを取り扱う。なお、Propsは`Chapter 6`、Stateは`Chapter 7`で説明する。

#### Components(コンポーネント）

UIは、`コンポーネント`と呼ばれる小さな部品に分解することができる。コンポーネントはレゴブロックのようなもので、部品を組み合わせることでより大きな構造を作り出すことができる。
あるコンポーネントを変更しても他のコンポーネントには影響しないため、UIのコードが大きくなっても保守がしやすい。

##### コンポーネントの作成

Reactでは、コンポーネントは**UI要素を返す関数**。

コンポーネントの関数（ここでは`Header`）を作り、retuen文にJSXを記載する。コンポーネント名は先頭を大文字にすることで、プレーンなHTMLやJavaScriptと区別する。

コンポーネントは`root.render()`に渡してレンダリングする。コンポーネントを渡す際は、通常のHTMLタグと同じように角括弧`<>`で囲う。

```html: index.html
<script type="text/jsx">
  const app = document.getElementById("app")
 
  function Header() {
     return <h1>Develop. Preview. Ship.</h1>;
   }
 
  const root = ReactDOM.createRoot(app);
  root.render(<Header />);
</script>
```

##### コンポーネントの入れ子

通常のHTML要素と同様に、Reactコンポーネント同士を**ネスト**（**入れ子**）にすることができる。

下記の例では、`HomePage`というコンポーネントの中に`Header`というコンポーネントが含まれる。

```javascript
function Header() {
  return <h1>Develop. Preview. Ship.</h1>;
}
 
function HomePage() {
  return (
    <div>
      {/* Nesting the Header component */}
      <Header />
    </div>
  );
}
 
const root = ReactDOM.createRoot(app);
root.render(<Header />);
```

##### コンポーネントツリー

`HomePage`の子として`Header`,`Article`,`Footer`があり、`Header`の子として`Logo`,`Title`,`Nav`がある、というようにネストを繰り返すことで、コンポーネントはツリー構造を形成することができる。

![ComponentTree.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760224/21fd38d2-1074-5518-5a87-37476a7b3ab6.png)

### Chapter 6: Propsを使ったデータ表示

https://nextjs.org/learn/react-foundations/displaying-data-with-props

通常のHTML要素には属性があり、その値により要素の動作や見た目を変えられるが、Reactのコンポーネントでも同じことができる。コンポーネントに渡すプロパティは`props`と呼ばれる。（単数なら`prop`）

JavaScriptの関数に引数を渡せるのと同様に、レンダリング時の表示内容をpropsで渡すことができる。これらのpropsは親コンポーネントから子コンポーネントへ渡される。

注：Reactでは、データはコンポーネントツリーを流れていく。これは一方向データフローと呼ばれる。Chapter 7で説明するステートは、propsとして親コンポーネントから子コンポーネントに渡される。

#### propsの使用

下記の`HomePage`コンポーネントでは、`title`というプロパティを`Header`コンポーネントに渡している。

```javascript
function HomePage() {
  return (
    <div>
      <Header title="React" />
    </div>
  );
}
```

そして、子コンポーネントである`Header`は、その**最初の関数パラメータ**としてpropsを受け取る。

propsの内容を`console.log()`で出力すると、`{ title: "React" }`という形式になっており、`title`というプロパティを持つオブジェクトであることがわかる。

```javascript
function Header(props) {
  console.log(props); // { title: "React" }
  return <h1>Develop. Preview. Ship.</h1>;
}
```

オブジェクトの分割を使用して、関数のパラメータ内でpropsの値を明示的に指定すれば、`<h1>`タグの内容を`title`変数に置き換えることができる。

```javascript
function Header({ title }) {
  console.log(title); // "React"
  return <h1>title</h1>;
}
```

これを実行すると、そのまま"title"と表示されてしまう。これは、`<h1>title</h1>`では、ただ単にプレーンな文字列をレンダリングしていると捉えられる（`title`が変数だと見なされない）ため。


#### JSXで変数を使用する

`title`をJavaScriptの変数と認識させるには、中括弧`{}`を追加する。
これらは特別なJSX構文で、JSXマークアップの中に通常のJavaScriptを直接書くことができる。

```javascript
function Header({ title }) {
  console.log(title);
  return <h1>{title}</h1>;
}
```

中括弧`{}`の中には、任意のJavaScript式（1つの値として評価されるもの）を追加できる。例えば:

1.ドット表記を用いたオブジェクトプロパティの参照

```javascript
function Header(props) {
  return <h1>{props.title}</h1>;
}
```

2.テンプレートリテラル

```javascript
function Header({ title }) {
  return <h1>{`Cool ${title}`}</h1>;
}
```

3.関数の戻り値

```javascript
function createTitle(title) {
  if (title) {
    return title;
  } else {
    return 'Default title';
  }
}
 
function Header({ title }) {
  return <h1>{createTitle(title)}</h1>;
}
```

4.三項演算子

```javascript
function Header({ title }) {
  return <h1>{title ? title : 'Default Title'}</h1>;
}
```

三項演算子を使用する場合、デフォルト値が設定されているため、親コンポーネントから`title`が渡されなくても構わない。

```javascript
function Header({ title }) {
  return <h1>{title ? title : 'Default title'}</h1>;
}
 
function HomePage() {
  return (
    <div>
      <Header />
    </div>
  );
}
```

#### リストの繰り返し処理

データをリストで表示したい場面はよくある。
配列メソッドを使用して、データ操作や、同じスタイルだが情報が異なるUI要素を生成することができる。

以下は、`HomePage`コンポーネントに追加された配列を`array.map()`で繰り返し処理し、アロー関数でリスト項目にマッピングする例。

```javascript
function HomePage() {
  const names = ['Ada Lovelace', 'Grace Hopper', 'Margaret Hamilton'];
 
  return (
    <div>
      <Header title="Develop. Preview. Ship." />
      <ul>
        {names.map((name) => (
          <li key={name}>{name}</li>
        ))}
      </ul>
    </div>
  );
}
```

### Chapter 7: ステートによるインタラクティブ性の追加

https://nextjs.org/learn/react-foundations/updating-state

このChapterでは、Reactで**ステート**と**イベントハンドラー**を使って、インタラクティビティーを追加する方法を見ていく。

例として、`HomePage`コンポーネントの中に"Like"ボタンを加えてみる。
まず`return`文にボタンを追加する。

```javascript
function HomePage() {
  // ...
  return (
    <div>
      {/* ... */}
      <button>Like</button>
    </div>
  );
}
```


#### イベントの呼び出し

ボタンがクリックされたときに何らかの動作をさせるには、`onClick`イベントを使用する。Reactでは、イベント名はキャメルケースで表す。

```javascript
function HomePage() {
  // ...
  return (
    <div>
      {/* ... */}
      <button onClick={}>Like</button>
    </div>
  );
}
```


#### イベントのハンドリング

イベントが呼び出された際のハンドリング関数を作成できる。`handleClick()`という関数を作成しておき、`onClick`イベントが発生したらその関数を呼び出す。

```javascript
function HomePage() {
  // 	...
  function handleClick() {
    console.log('increment like count');
  }
 
  return (
    <div>
      {/* ... */}
      <button onClick={handleClick}>Like</button>
    </div>
  );
}
```

#### ステート（状態）とフック

Reactにはフック（`hook`）と呼ばれる特別な関数がある。

フックを使えば、ステート（状態）のような追加ロジックをコンポーネントに追加できる。ステートとは状況に応じて変化するUIの情報のこと。通常はユーザーの操作によって変化する。
例えば、ステートを使って、ユーザーが「いいね」ボタンをクリックした回数を記憶することができる。

ステートを管理するためのフックの名前は`useState()`。
`useState()`は配列を返す。
最初の項目は"値"で、配列の2番目の項目は"値を更新する関数"。
どちらも名前は自由に付けられるが、値を更新する関数は`set`を使うのが一般的。例えば値が`likes`であれば、それを更新する関数は`setLikes`とする。

```javascript
function HomePage() {
  // ...
  const [likes, setLikes] = React.useState();
 
  // ...
}
```

`likes`ステートの初期値を設定することもできる:

```javascript
function HomePage() {
  // ...
  const [likes, setLikes] = React.useState(0);
}
```

ここまでの要素を、「いいね」ボタンに反映させるとこうなる。
`handleClick`関数には「いいね」のカウントを1増やす処理を持たせ、ボタンには現在のいいねの数が表示される。

```javascript
function HomePage() {
  // ...
  const [likes, setLikes] = React.useState(0);
 
  function handleClick() {
    setLikes(likes + 1);
  }
 
  return (
    <div>
      {/* ... */}
      <button onClick={handleClick}>Likes ({likes})</button>
    </div>
  );
}
```

ここまでの情報はStateの基礎なので、より深く理解したければ、Reactのドキュメントを参照する[^state]。

[^state]: Adding Interactivity (https://react.dev/learn/adding-interactivity) や Managing State (https://react.dev/learn/managing-state) を参照


### Chapter 8: ReactからNext.jsへ

https://nextjs.org/learn/react-foundations/from-react-to-nextjs

ここまでの章でReactについて学んできた。
ReactはUIの構築に秀でたライブラリだが、アプリケーションを構築するためにはある程度の作業が必要になる。また、新しいReactの機能の一部には、フレームワークを必要とするものもある。

Next.jsを使うことで、Reactを使う上での手間を減らし、さらに便利な機能を利用できる。

次のChapterからはNext.jsについて説明する。


### Chapter 9: Next.jsのインストール

https://nextjs.org/learn/react-foundations/installation

Next.jsを使用するには、Node.jsの`18.17.0`以上が必要となる。

Next.jsでは、`Chapter 4`で説明した、`unpkg.com`からの`react`と`react-dom`の読み込みは必要ない。
代わりに、npmなどのパッケージマネージャを使用して、`next` `react` `react-dom` をインストールする。

これにより、Reactの説明で作成したサンプルをシンプルにすることができる。
JSXだけが残るので、拡張子も`.html`ではなく`.js`（または`.jsx`）に変えられる。

```javascript: index.js
import { useState } from 'react';
 
function Header({ title }) {
  return <h1>{title ? title : 'Default title'}</h1>;
}
 
function HomePage() {
  const names = ['Ada Lovelace', 'Grace Hopper', 'Margaret Hamilton'];
 
  const [likes, setLikes] = useState(0);
 
  function handleClick() {
    setLikes(likes + 1);
  }
 
  return (
    <div>
      <Header title="Develop. Preview. Ship." />
      <ul>
        {names.map((name) => (
          <li key={name}>{name}</li>
        ))}
      </ul>
 
      <button onClick={handleClick}>Like ({likes})</button>
    </div>
  );
}
```

#### 最初のページを作成

Next.jsは、フォルダやファイルの構造や名称がそのままURLになる「ファイルシステムルーティング」を使用しており、ルーティングがシンプルである。

Next.jsで最初のページを作成する方法は下記の通り

1. `app`という新しいフォルダを作成し、`index.js`ファイルをその中に移動する
1. ファイル名を`page.js`にリネームする。これがアプリケーションのメインページとなる
1. Next.jsがページのメインコンポーネントを区別できるよう、`<HomePage>`コンポーネントに`export default`を付与する

これらを反映させると下記の通りになる

```javascript: app/page.js
import { useState } from 'react';
 
function Header({ title }) {
  return <h1>{title ? title : 'Default title'}</h1>;
}
 
export default function HomePage() {
  // ...
}
```

#### 開発サーバーの実行

開発サーバーを動かしてみよう。
開発中にページの新しい変更を確認できるようにするため、`package.json`ファイルに"next dev"スクリプトを追加する。

```json: package.json
{
  "scripts": {
    "dev": "next dev"
  },
  "dependencies": {
    ...
  }
}
```

ターミナルで`npm run dev`を実行すると、下記の2つのことがわかる。

(1) `localhost:3000`にアクセスすると、次のエラーが表示される。

![スクリーンショット 2024-08-16 1.13.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760224/28bbcd12-203e-7985-0b3f-074a1df6d092.png)

これは、Reactのコンポーネントをサーバー上でレンダリングできるようにする「サーバーコンポーネント」をNext.jsが使用しているため。
サーバーコンポーネントは`useState`をサポートしていないため、エラーになっている。

これについては次のChapter 10で説明する。


(2) `layout.js`という新しいファイルが`app`フォルダ内に自動的に作成されている。

これはアプリケーションのメインレイアウトを管理するもの。すべてのページで共有されるUI要素（ナビゲーション、フッターなど）を追加するために使用する。

```javascript: app/layout.js
export const metadata = {
  title: 'Next.js',
  description: 'Generated by Next.js',
};
 
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
```

### Chapter 10: サーバーコンポーネントとクライアントコンポーネント

https://nextjs.org/learn/react-foundations/server-and-client-components


#### ネットワーク境界

Next.jsではReactの**サーバーコンポーネント**と**クライアントコンポーネント**をサポートしている。
それぞれの違いは追って説明するが、`ネットワーク境界`は、特に`Next.js`などのフレームワークにおいて、設計時にサーバーコンポーネントとクライアントコンポーネントを分離する際の概念的な境界線を指す。

ネットワーク境界を設定することで、重い処理はサーバーで効率的に行い、クライアントには軽量なデータのみを渡すなど、パフォーマンスやユーザー体験の最適化が行える。

それぞれのコンポーネントについて説明する。

**クライアントコンポーネント**は、従来のReactコンポーネント。
ブラウザ上でJavaScriptを実行して、ユーザーの操作に応じたリアルタイムでのUI更新など、動的な（インタラクティブな）UIを提供する。

**サーバーコンポーネント**はサーバーサイドで（Node.jsなどで）実行されるコンポーネント。React 18から導入されたもので、扱うには基本的にNext.jsなどのフレームワークによるサポートが必要。
サーバーサイドで実行できるため、データベースやファイルシステムに直接アクセスできるのが特徴。
動的な要素を持たない静的なコンテンツや、サーバーサイドでの重い処理が必要なコンポーネントをサーバーコンポーネントで扱う。
サーバーコンポーネントはJavaScriptのバンドルに含まれないためネットワーク負荷が軽減されることや、ページの初期表示に使用されるためSEOが改善されることもメリット。

サーバーコンポーネントがレンダリングされると、**React Server Component Payload**（**RSC ペイロード**）と呼ばれる特殊なデータがクライアントに送られる。

RSCペイロードには、主に次の2つの情報が含まれる：

1. サーバーコンポーネントのレンダリング結果
1. クライアントコンポーネントがレンダリングされる箇所のプレースホルダと、それに対応するJavaScriptファイルへの参照


Reactはこれらの情報を使ってサーバーコンポーネントとクライアントコンポーネントを統合し、クライアントのDOMを更新する。


#### クライアントコンポーネントの使用

Chapter 9でも触れたが、Next.jsはデフォルトでサーバーコンポーネントを使用する。これはアプリケーションのパフォーマンスを向上させるため。

Chapter 9で発生していたエラーを振り返ると、サーバーコンポーネント内で`useState`を使用しようとしていることが原因だった。「いいね」ボタンにはインタラクティブ性が必要なため、クライアントコンポーネントに移動する必要がある。

まず`app`フォルダ内に`like-button.js`という新しいファイルを作成し、`LikeButton`コンポーネントをエクスポートする：

```javascript: app/like-button.js
export default function LikeButton() {}
```

`<button>`要素と`handleClick()`関数を`page.js`から新しい`LikeButton`コンポーネントに移動する：

```javascript: app/like-button.js
export default function LikeButton() {
  function handleClick() {
    setLikes(likes + 1);
  }
 
  return <button onClick={handleClick}>Like ({likes})</button>;
}
```

次に、`likes`のステートとインポート文を移動させる：

```javascript: app/like-button.js
import { useState } from 'react';
 
export default function LikeButton() {
  const [likes, setLikes] = useState(0);
 
  function handleClick() {
    setLikes(likes + 1);
  }
 
  return <button onClick={handleClick}>Like ({likes})</button>;
}
```

そして、`LikeButton`をクライアントコンポーネントにするには、ファイルの先頭にReactの'use client'ディレクティブを追加する。


```javascript: app/like-button.js
'use client';
 
import { useState } from 'react';
 
export default function LikeButton() {
  const [likes, setLikes] = useState(0);
 
  function handleClick() {
    setLikes(likes + 1);
  }
 
  return <button onClick={handleClick}>Like ({likes})</button>;
}
```

page.jsファイルに戻り、`LikeButton`コンポーネントをページでインポートする：


```javascript: app/page.js
import LikeButton from './like-button';
 
function Header({ title }) {
  return <h1>{title ? title : 'Default title'}</h1>;
}
 
export default function HomePage() {
  const names = ['Ada Lovelace', 'Grace Hopper', 'Margaret Hamilton'];
 
  return (
    <div>
      <Header title="Develop. Preview. Ship." />
      <ul>
        {names.map((name) => (
          <li key={name}>{name}</li>
        ))}
      </ul>
      <LikeButton />
    </div>
  );
}
```

両方のファイルを保存し、ブラウザでアプリを表示する。
エラーがなくなったので、変更を加えて保存すると、変更を反映するためにブラウザが自動的に更新されることに気づくはず。
これはFast Refreshと呼ばれる機能で、Next.jsにあらかじめ設定されているもの。

### Chapter 11: 次のステップ

https://nextjs.org/learn/react-foundations/next-steps

ここまでで、このチュートリアルは終わり。

Reactの学習を継続したければ、世の中には多くの優良なコンテンツがあるが、[React Documentation](https://react.dev/)もその1つ。インタラクティブなサンドボックスで練習しながら学ぶことができる。

ダッシュボードアプリを作りながらNext.jsについて学びたければ、[Next.jsのチュートリアル](https://nextjs.org/learn/dashboard-app)に移ろう。このチュートリアルよりも複雑なプロジェクトを作りながら、Next.jsの主な機能を学ぶことができる。