React Foundationsをやったときのメモです。
ソースコードはこちら
4. Reactを始めよう
初めてのReact
index.html
<html>
<body>
<div id="app"></div>
<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 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>
</body>
</html>
プレーンなJavaScriptでDOMを操作する代わりに、 ReactDOM.createRoot() を使用して特定のDOM要素をターゲットにして、Reactコンポーネントを表示するためのルートを作成します。
次に root.render() でReactコードをDOMにレンダリングします。
index.html
<html>
<body>
<div id="app"></div>
<!-- Load React. -->
<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 type="text/jsx">
const domNode = document.getElementById("app");
const root = ReactDOM.createRoot(domNode);
root.render(<h1>Develop. Preview. Ship.</h1>);
</script>
</body>
</html>
このコードをブラウザで実行しようとするとエラーが発生します。
Uncaught SyntaxError: expected expression, got '<'
この部分が有効なJavaScriptではないからです。このコードはJSXです。
<h1>Develop. Preview. Ship.</h1>
JSXとは
JSXは "JavaScript XML" の略で、JavaScriptの構文を拡張したものです。これにより、JavaScriptのコード内にHTMLのようなマークアップを記述することができます。
もともとはReactのために開発されましたが、現在は他のフレームワークでも利用されています。
仕組み
JSXで書かれたコードは、ブラウザで直接実行されるわけではありません。BabelのようなトランスパイラによってJavaScript(具体的には React.createElement() という関数呼び出し)に変換されてから実行されます。
Reactとの関係
Reactにおいて必須ではないですが、コンポーネントの構造を視覚的にわかりやすく記述できため、一般的には採用されます。使わない場合は React.createElement() を何度も呼び出すことになります。
基本的な構文ルール
- 単一のルート要素を返す。
- 複数の要素となる場合はそれを
<div>などでラッピングする必要があります。
- 複数の要素となる場合はそれを
- すべてのタグを閉じる
-
<img>は<img />と記述する必要があります。 -
<li>orangeは<li>orange</li>と記述する必要があります。
-
- すべてのものをキャメルケースで記述
- JSXはトランスパイルされるとJavaScriptオブジェクトのキーとなるためです。
- ただし、
aria-*data-*属性はHTMLのダッシュを使って記述されます。
Babelを追加する
index.html
<html>
<body>
<div id="app"></div>
<!-- Load React. -->
<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>
<!-- Load Babel Compiler. -->
<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>
</body>
</html>
5. コンポーネントを使ったUIの構築
Reactのコアコンセプト
- Component
- Props
- State
Component
ReactではコンポーネントはUI要素(JSX)を返す関数です。
関数の定義と呼び出しには、いくつかの決まりがあります
- Reactコンポーネントは大文字から始める必要があります。
function Header() { return <h1>Hello</h1> }
- Reactコンポーネントは
<Header />のように呼び出します。
component_1.html
<html>
<body>
<div id="app"></div>
<!-- Load React. -->
<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>
<!-- Load Babel Compiler. -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/jsx">
const app = document.getElementById("app");
// React Component
function Header() {
return <h1>Develop. Preview. Ship.</h1>;
}
const root = ReactDOM.createRoot(app);
root.render(<Header />);
</script>
</body>
</html>
コンポーネントのネスト
Reactコンポーネントはネストすることも可能です。
component_2.html
<html>
<body>
<div id="app"></div>
<!-- Load React. -->
<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>
<!-- Load Babel Compiler. -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/jsx">
const app = document.getElementById("app");
// React Component
function Header() {
return <h1>Develop. Preview. Ship.</h1>;
}
function HomePage() {
return <div>
{/* JSX のコメント */}
<Header />
</div>
}
const root = ReactDOM.createRoot(app);
root.render(<HomePage />);
</script>
</body>
</html>
6. Propsでデータを表示する
Props
Reactコンポーネントにプロパティとして情報を渡すための仕組みが props です。
propsを使ってみる
コンポーネントではHTML属性と同様にプロパティを指定することができます。
function HomePage() {
return (
<div>
<Header title="React" />
</div>
);
}
htmlの class 属性を指定したい場合は className を利用します。 (JavaScriptの予約後であるため)
<h1 className="text-blue-500">I'm blue!</h1>
propsを渡されたコンポーネントは、それを関数の引数として受け取ることができます。
function Header(props) {
return <h1>Develop. Preview. Ship.</h1>;
}
propsはオブジェクトなので分割代入が可能です。
function Header({ title }) {
return <h1>Develop. Preview. Ship.</h1>;
}
JSXでの変数の使用
1. 変数の展開には {} を使います。
function Header(props) {
return <h1>{props.title}</h1>;
}
2. テンプレートリテラル内での展開はJSと同じ
function Header({ title }) {
return <h1>{`Cool ${title}`}</h1>;
}
3. 関数の戻り値をそのまま展開
function createTitle(title) {
if (title) {
return title;
} else {
return 'Default title';
}
}
function Header({ title }) {
return <h1>{createTitle(title)}</h1>;
}
4. 三項演算子
function Header({ title }) {
return <h1>{title ? title : 'Default Title'}</h1>;
}
function Header({ title }) {
return <h1>{title ? title : 'Default title'}</h1>;
}
function HomePage() {
return (
<div>
<Header />
<Header title="React" />
</div>
);
}
配列の反復処理
function HomePage() {
const name = ["Ada Lovelance", "Grace Hopper", "Margaret Hamilton"];
return (
<div>
<Header title="Develop. Preview. Ship." />
<ul>
{
names.map((name) => (
<li>{name}</li>
))
}
</ul>
</div>
);
}
このコードを実行するとReactは key プロパティが不足しているという警告を出します。
これは ReactがDOM内のどの要素を更新すべきかを判断するために配列要素を位置位に識別するなにかを必要としているからです。
function HomePage() {
const name = ["Ada Lovelance", "Grace Hopper", "Margaret Hamilton"];
return (
<div>
<Header title="Develop. Preview. Ship." />
<ul>
{
names.map((name, i) => (
<li key={i}>{name}</li>
))
}
</ul>
</div>
);
}
7. 状態によるインタラクティブ性の追加
イベントのリスニング
onClick onChange onSubmit など、Reactでは、は、イベント名はキャメルケースで表記されます。
function HomePage() {
return (
<div>
<button onClick={}>Like</button>
</div>
);
}
イベントの処理
イベントにはイベントハンドラを設定することができます。
function HomePage() {
// イベントハンドラ
function handleClick() {
console.log('increment like count');
}
return (
<div>
{/* handleCheck イベントハンドラを設定 */}
<button onClick={handleClick}>Like</button>
</div>
);
}
状態とフック
Reactにはフックと呼ばれる一連の関数があります。フックを使用すると、コンポーネントに状態などの追加ロジックを実装できます。
例えば、イイネボタンのクリック回数を保存・インクリメントするには state を利用できます。状態を管理するReactフックは useState() と呼ばれます。
React.useState() の引数
- デフォルト値
React.useState() の戻り値
- 最初の要素は状態(
value)であり、任意の名前をつけることができます。 - 2番目の要素は状態を更新する関数(
update)で、任意の名前をつけることができます。(一般的にvalueの名称の先頭にsetをつけます。)
function HomePage() {
const [likes, setLikes] = React.useState(0);
}
onClick イベントで likes ステートの値を更新するイベントハンドラ handleClick を実装します。
function HomePage() {
const [likes, setLikes] = React.useState(0);
function handleClick() {
setLikes(likes + 1);
}
return (
<div>
<button onClick={handleClick}>Likes ({likes})</button>
</div>
);
}
8. ReactからNext.jsへ
9. Next.jsのインストール
NextJSのインストール
npm install react@latest react-dom@latest next@latest
mkdir app
ページの実装
app/page.js
import { useState } from 'react';
function Header({ title }) {
return <h1>{title ? title : "Default title"}</h1>;
}
// export defaultを追加すると、Next.jsがページのメインコンポーネントとしてレンダリングするコンポーネントを区別できるようになります。
export default function HomePage() {
const names = ["Alice", "Bob", "Charlie"];
const [likes, setLikes] = useState(0);
function handleClick() {
setLikes(likes + 1);
}
return (
<div>
<Header title="Develop. Preview. Ship." />
<ul>
{
names.map((name, i) => (
<li key={i}>{name}</li>
))
}
</ul>
<button onClick={handleClick}>Like ({likes})</button>
</div>
)
}
開発サーバーの起動
package.json
{
// scriptsを追加
"scripts": {
"dev": "next dev"
},
"dependencies": {
"next": "^14.0.3",
"react": "^18.3.1",
"react-dom": "^18.3.1"
}
}
npm run dev
http;//localhost:3000 にアクセスすると以下のエラーが表示されます。
./app/page.js
## Error Type
Build Error
## Error Message
× You're importing a component that needs `useState`. This React Hook only works in a Client Component. To fix, mark the file (or its parent) with the `"use client"` directive.
## Build Output
./app/page.js
Error: × You're importing a component that needs `useState`. This React Hook only works in a Client Component. To fix, mark the file (or its parent) with the `"use client"` directive.
│
│ Learn more: https://nextjs.org/docs/app/api-reference/directives/use-client
│
╭─[/workspaces/react-learn/react_tutorial/9_installing_nextjs/app/page.js:1:1]
1 │ import { useState } from 'react';
· ────────
2 │
3 │ function Header({ title }) {
4 │ return <h1>{title ? title : "Default title"}</h1>;
╰────
Next.js version: 15.5.4 (Webpack)
これは、Next.jsがReact Server Componentsという新機能を使用しているためです。この機能により、Reactがサーバー上でレンダリングできるようになります。Server ComponentsはuseStateをサポートしていないため、代わりにClient Componentを使用する必要があります。
次の章では、Server ComponentsとClient Componentsの主な違いについて説明し、このエラーを修正します。
layout.js
サーバーを起動すると app/layout.js が自動生成されます。これはアプリケーションのメインレイアウトです。全ページで共有されるUI要素(例:ナビゲーション、フッターなど)を追加するために使用できます。
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>
)
}
10. サーバーおよびクライアントコンポーネント
サーバーとクライアント
それぞれの環境には、独自の機能と制約があります。例えば、サーバーサイドでレンダリングを行うと、クライアントからのAPIリクエストを削減できるため、パフォーマンスを向上させることができます。しかし、UIをインタラクティブにするには、クライアント側でDOMを更新する必要があります。
そのため、サーバーとクライアント向けに記述するコードは必ずしも同じではありません。特定の操作(データの取得やユーザー状態の管理など)は、どちらか一方の環境の方が適している場合があります。
ネットワーク境界
ネットワーク境界とは、異なる環境を分離する概念的な境界線です。
Reactでは、コンポーネントツリー内のネットワーク境界を配置する場所を選択します。
例えば、サーバー側でデータを取得しユーザーの投稿をレンダリング(サーバーコンポーネント)し、クライアント側で各投稿のインタラクティブなLikeButtonをレンダリング(クライアントコンポーネント)といった具合です。
同様に、サーバーでレンダリングされページ間で共有されるNavコンポーネントを作成できますが、リンクのアクティブ状態を表示したい場合は、リンクのリストをクライアント側でレンダリングできます。
裏側では、コンポーネントは2つのモジュールグラフに分割されます。
-
サーバーモジュールグラフ(ツリー)
サーバー上でレンダリングされるすべてのサーバーコンポーネントが含まれます。 -
クライアントモジュールグラフ(ツリー)
すべてのクライアントコンポーネントが含まれます。
サーバーコンポーネントがレンダリングされた後、React Server Component Payload(RSC) と呼ばれる特殊なデータ形式がクライアントに送信されます。RSCペイロードには以下が含まれます:
- サーバーコンポーネントのレンダリング結果
- クライアントコンポーネントがレンダリングされるべき位置のプレースホルダーと、それらのJavaScriptファイルへの参照
Reactはこの情報を使用してサーバーコンポーネントとクライアントコンポーネントを統合し、クライアント側のDOMを更新します。
クライアントコンポーネントの使用
Next.jsはデフォルトでサーバーコンポーネントを使用します。クライアントコンポーネントを利用するには明示的な宣言が必要です。
先程のエラーを確認すると、Next.jsはサーバーコンポーネント内で useState にアクセスしようとしていることを警告しています。
新たに LikeButton コンポーネントをエクスポートするファイル app/components/like-button.js を作成します。
app/components/like-button.js
'use client'; // クライアント側でコンポーネントをレンダリングすることを明示
import { useState } from 'react';
export default function LikeButton() {
const [likes, setLikes] = useState(0)
function handleClick() {
setLikes(like + 1);
}
return <button onClick={handleClick}>like ({like})</button>;
}
app/page.js
// LikeButtonはデフォルトエクスポートされているので {} は不要
import LikeButton from './components/like-button';
function Header({ title }) {
return <h1>{title ? title : "Default title"}</h1>;
}
export default function HomePage() {
const names = ["Alice", "Bob", "Charlie"];
return (
<div>
<Header title="Develop. Preview. Ship." />
<ul>
{
names.map((name, i) => (
<li key={i}>{name}</li>
))
}
</ul>
<LikeButton />
</div>
)
}
※デフォルトエクスポートと名前付きエクスポート
-
import LikeButton from './components/like-button';- デフォルトエクスポート されたものをインポートするための書き方です。
-
import { LikeButton } from './components/like-button';-
名前付きエクスポート されたものを
{}で指定してインポートするための書き方です。
-
名前付きエクスポート されたものを

