Reactを独学で学ぶ上で、いちばん最初に公式ドキュメントの理解から着手しようと思ったのでアウトプットも兼ねて内容をまとめました。
ここではReact公式ドキュメントの「クイックスタート」をまとめました。
こんな人向け
- Reactを触ったことがない人
- Reactに興味があり、学ぼうとしている
- Reactの概念・仕組みを知りたい
1.Reactとは
Reactは、Facebookによって開発されたオープンソースのJavaScriptライブラリです。
ユーザインタフェースを構築するために使用され、
「コンポーネント」と呼ばれる小さく独立した部品から組み立てることができます。
2.コンポーネント
Reactアプリはコンポーネントで構成されています。コンポーネントとは、独自のロジックと外見を持つUIの部品のことです。コンポーネントは、ボタンのような小さなものである場合も、ページ全体を表す大きなものである場合もあります
Reactにおけるコンポーネントとは、マークアップを返すJavaScript関数のことです。
function MyButton() {
return (
<button>I'm a button</button>
);
}
<MyButton />
を宣言すると、別のコンポーネントにネストできます。
export default funciton MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton /> // 宣言
</div>
);
}
<MyButton />のように大文字で始めることによって、Reactのコンポーネントであるということを示しています。Reactのコンポーネント名は常に大文字で始める必要があり、HTMLタグは小文字でなければいけません。
ですので下記のように書く必要があります。
function MyButton() {
return (
<button>
I'm a button
</button>
);
}
export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
export defaultってなに?となる人はexport defaultってなんだろうを見ると分かりやすいです。
3.Reactで使用するマークアップ言語JSX
上で見たマークアップ言語は、JSXと呼ばれるものです。
JSXはHTMLより構文が厳格で、<br />のようにタグは閉じる必要があります。また、コンポーネントは複数のJSXタグをreturnすることはできません。<div>...</div> や空の <>...</> ラッパのような共通の親要素で囲む必要があります。
function AboutPage() {
return (
<>
<h1>About</h1>
<p>Hello there.<br />How do you do?</p>
</>
);
}
スタイルの追加
Reactでは、CSSクラスを className
で指定します。HTMLの class
属性と同じ方法で動作します。
<img className="avatar" />
スタイルは別のCSSファイルに対応するCSSルールを記述します。
.avatar {
border-radius: 50%;
}
ReactにはCSSファイルの追加方法に関する規則はありません。
もし、CSSの記法を確認しながらコーディングしたい場合、HTMLに <link>
タグを追加しておくと便利です。
データの表示
JSXを使用することで、JavaScript内にマークアップを入れることができます。{}
を使うことで、JavaScriptに「戻る」ことができ、コード内の変数を埋め込んでユーザに表示することができます。
たとえば、以下は user.name
を表示します。
return (
<h1>
{user.name}
</h1>
);
JSXの属性の部分からでもJavaScriptに「戻る」こともできます。
こちらも{}
を使用する必要があります。例えば、className="avator"
はCSSクラスとして "avator"
文字列を渡すものですが、src={user.imageUrl}
はJavaScriptの user.imageUrl
変数の値を読み込み、その値を src
属性として渡します。
return (
<img
className="avatar"
src={user.imageUrl}
/>
);
JSXの{}
の中にもっと複雑な指揮を入れることもできます。例えば、文字列の連結ができます。
const user = {
name: 'Hedy Lamarr',
imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
imageSize: 90,
};
export default function Profile() {
return (
<>
<h1>{user.name}</h1>
<img
className="avatar"
src={user.imageUrl}
alt={'Photo of ' + user.name}
style={{
width: user.imageSize,
height: user.imageSize
}}
/>
</>
);
}
上の例では style={{}}
は特別な構文ではなく、style={ }
というJSXの波括弧内にある{}
オブジェクトです。スタイルがJavaSript変数に依存する場合は、style
属性を使うことができます。
条件付きレンダー
Reactには、条件分岐を書くための特別な構文は存在しません。代わりに、通常のJavaScriptコードを書くときに使うのと同じ手法を使います。例えば、if
ステートメントを使って条件付きでJSXを含めることができます。
let content;
if (isLoggedIn) {
content = <AsminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
</div>
);
もっとコンパクトにしたい場合、条件 ? 演算子
を使用できます。if
とは異なり、JSXの中で動作します。
<div>
{isLoggedIn ? (
<AsminPanel />
) : (
<LoginForm />
)}
</div>
else
側の分岐が不要な場合は、短い 論理 && 構文
を使用することもできます。
<div>
{isLoggedIn && <AdminPanel />}
</div>
これらは、属性を条件付きで指定する場合に役立ちます。
リストのレンダー
コンポーネントのリストをレンダーする場合は、
forループ
や配列のmap()関数
といったJavaScriptの機能を使って行います。
例えばこのような商品の配列があるとします。
const products = [
{ title: 'Cabbage', id: 1 },
{ title: 'Garlic', id: 2 },
{ title: 'Apple', id: 3 },
];
コンポーネント内で、map()
関数を使って商品の配列を <li>
要素の配列に変換します。
const listItems = products.map(product =>
<li key={product.id}>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
<li>
に key
属性があることに注意してください。リスト内の各項目には、兄弟の中でそれを一意に識別するための文字列または数値を渡す必要があります。( key
はデータベース上のIDなどが該当します)
リストの中身を追加・削除した場合、Reactは key
でそれを識別します。
const products = [
{ title: 'Cabbage', isFruit: false, id: 1 },
{ title: 'Garlic', isFruit: false, id: 2 },
{ title: 'Apple', isFruit: true, id: 3 },
];
export default function ShoppingList() {
const listItems = products.map(product =>
<li
key={product.id}
style={{
color: product.isFruit ? 'magenta' : 'darkgreen'
}}
>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
}
イベントに応答する
コンポーネントの中でイベントハンドラ関数を宣言することで、イベントに応答できます。
function MyButton() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}
onClick={handleClick}
の末尾に括弧がないことに注意して下さい!
ここはイベントハンドラ関数を呼び出すのではなく、渡すだけです。ユーザがボタンをクリックしたときに、Reactがイベントハンドラを呼びます。
画面の更新
例えば、ボタンがクリックされた回数を数えて表示する画面があるとします。これを行うには、コンポーネントにstateを追加する必要があります。
まず、Reactから useState
をインポートします。
import { useState } from 'react';
これで、コンポーネント内にstate変数を宣言できます。
function MyButton() {
const [count, setCount] = useState(0);
// ...
useState
からは2つのものが得られます。現在のstate(count
)と、それを更新するための関数(setCount
)です。名前は何でも構いませんが、慣習的には[somthing, setSomething]
のように記述します。
ボタンが初めて表示されるとき、
count
は0
になります。これはuseState()
に0
を渡したからです。
stateを変更したいときは、setCount()
を呼び出し、新しい値を渡します。このボタンをクリックすると、カウンタが増加します。
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
React は、再度コンポーネントの関数を呼び出します。今度は count が 1 になっています。次の呼び出しでは 2 になっています。次々と増えていきます。
同じコンポーネントを複数の場所でレンダーした場合、それぞれが独自の state を持ちます。それぞれのボタンを個別にクリックしてみてください:
import { useState } from 'react';
export default function MyApp() {
return (
<div>
<h1>Counters that update separately</h1>
<MyButton />
<MyButton />
</div>
);
}
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
各ボタンがそれぞれ count
というstateを「記憶」し、他のボタンに影響を与えないようになっています。
フックの利用
use
で始まる関数はフック(Hook)と呼ばれ、useState
はReactが提供する組み込みのフックです。(Reactの組み込みフック)
既存のフックを組み合わせて独自のフックの作成も可能です。
ただ、フックには通常の関数より制限があります。コンポーネントのトップレベル(または他のフック内)でのみ呼び出すことができます。
コンポーネント間でデータを共有する
前述の例では、それぞれの
MyButton
が独立したcount
を持っており、ボタンがクリックされるたびにクリックされたボタンのcount
だけが変更されました。
ただし、コンポーネント間でデータを共有し、常に一緒に更新したいということもよくあります。
両方のMyButton
コンポーネントが同じcount
を表示し、一緒に更新されるようにするには、状態を個々のボタンから「上に」移動して、それらすべてを含む最も近いコンポーネントに入れます。
このようにすると、どちらのボタンをクリックしても、MyApp
の count
が更新され、連動して MyButton
の両方のカウントが更新されます。
これらは下記のように表現します。
まず、MyButton
から MyApp
にstateの移動を行います。
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update separately</h1>
<MyButton />
<MyButton />
</div>
);
}
function MyButton() {
// ... we're moving code from here ...
}
次に、MyApp
から各 MyButton
にstateを渡し、共有のクリックハンドラも一緒に渡します。
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update together</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}
このように渡される情報は props と呼ばれます。
MyApp
コンポーネントはcount
状態とhandleClick
イベントハンドラを保持しており、それらをどちらも props として各ボタンに渡します。
最後に、MyButton
を変更して、親コンポーネントから渡された props を読み込むようにします。
function MyButton({count, onClick}) {
return (
<button onClick={onClick}>
Clicked {count} times
</button>
);
}
- ボタンをクリックすると、
onClick
ハンドラが発火します - 各ボタンの
onClick
プロパティはMyApp
内のhandleClick
関数となっているので、その中のコードが実行されます - そのコードは
setCount(count +1)
を呼び出し、count
というstate変数をインクリメントします - 新しい
count
の値が各ボタンに props として渡されるため、すべてのボタンに新しい値が表示されます
この手法は「stateのリフトアップ(持ち上げ)」と呼ばれています。リフトアップすることで、stateをコンポーネント間で共有できました!
import { useState } from 'react';
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update together</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}
function MyButton({ count, onClick }) {
return (
<button onClick={onClick}>
Clicked {count} times
</button>
);
}
これで、Reactの基本が分かったと思います。
お疲れさまでした。
ライセンス
本記事は、Reactの公式ドキュメント(React 公式サイトへのリンク)を参考に、Creative Commons Attribution 4.0 International License (CC BY 4.0) の下で公開されている情報に基づいて作成されています。