【速習React】Reactの基礎を完全マスターしよう!
はじめに
この記事は、JavaScriptの人気のフレームワークReactの基礎を一気におさらいしようという記事です。これからReactに入門しようという初心者の方へ向けて、React開発に必要な最低限の知識を紹介します。開発環境の構築から公開へ向けたビルド作業まで、Reactアプリ開発における一連の流れをしっかり抑えましょう。
React.jsの特徴
Reactは、公式の説明でユーザーインターフェース構築のためのJavaScriptライブラリとあるように、画面を構築する際に便利なJavaScriptライブラリです。
具体的に便利な点の1つとして、Viewを宣言的に構築できる点があります。これはコードを見るとイメージしやすいので後ほど紹介します。通常のJavaScriptのDOM操作では手続的と言われ「要素を作成する」「テキストを変更する」「要素を追加する」というように1つずつメソッドを呼び出すような手法を取ります。手続的な画面構築は、複雑なアプリケーションではやや記述量が多く大変です。
宣言的Viewと密接に関わるのがコンポーネント単位での画面構築です。ページを複数の部品に分解することで、複雑なアプリケーションでもコードの見通しよく開発できるようになります。
また、Reactはフロントエンドのライブラリですが、他にもサーバーサイドでの処理や、React Nativeなどの技術によってスマホアプリへも応用できます。Reactを一度学習すると幅広い分野で利用できるといったメリットがあります。
Reactの環境構築
Node.jsのインストール
Reactの開発ではNode.jsが必要になります。最新のLTS(長期サポート版)をインストールしておきましょう。
アプリの雛形を構築
アプリケーションを立ち上げる際、構築作業を一から行うと非常に手間がかかります。create-react-appというCLIツールはアプリケーションの雛形を作成してくれるため、簡単にプロジェクトを開始できます。
https://ja.reactjs.org/create-react-app.html
例えばmy-app
というアプリケーションを作成する場合は次のようになります。(my-app
の部分は自由に変更できます。)
npx create-react-app my-app
cd my-app
yarn start # npmを使う場合は `npm start`
これだけでアプリケーションの雛形が作成され、ブラウザで動作確認ができます。この状態でコードを編集すると自動でブラウザがリロードされるようになっており大変便利ですので、このまま開発に取り組みましょう。
では作成された雛形アプリケーションの中身を見ていきましょう。ソースファイルはsrc
ディレクトリ内に作成されApp.js
が開発の起点となります。また、package.json
にはインストールが行われたプロジェクトに必要なライブラリが記載されています。他にも開発を便利にするテストツールなどのファイルが含まれていますので、必要になり次第チェックしてください。
コンポーネント
コンポーネントとは何か
ここからはReact開発で必ず抑えておくべき4つの基礎を解説します。まず最初はコンポーネントです。コンポーネントとは、「UIを再利用可能な部品として定義」することです。再利用可能という部分は後ほど紹介します。まずは具体例でコンポーネントのイメージをつかみましょう。
コンポーネントの記述方法は大きく分けて2つあります。まずはClassコンポーネントです。
class HelloMessageClass extends React.Component {
render() {
return <div>Hello redimpulz</div>;
}
}
続いて関数コンポーネントです。機能はほぼ同じですがよりシンプルに記述できます。
const HelloMessageFunction = () => <div>Hello redimpulz</div>;
定義した2つのコンポーネントを使ってみましょう。Appから呼び出すことで画面に表示させることができます。
export default function App() {
return (
<div>
<HelloMessageClass />
<HelloMessageFunction />
</div>
);
}
このようにJavaScriptのコード中にHTMLタグがあるような書き方をJSXと言います。コンポーネントの内容を<div>Hello redimpulz</div>
というようにタグを用いて定義し、使いたいところで独自のHTMLタグのように記述します。これが「UIを宣言的に記述できる」というReactの特徴です。もしこれを手続的に実装しようとすると、div要素の作成→テキストを追加→親要素に挿入といった実装が必要になります。JSXを用いた宣言的UIによって、シンプルに記述できるようになっていることがわかります。
Classコンポーネント
ClassコンポーネントはJavaScriptのClass構文を用いた記法です。ポイントは、extends React.Component
と記述してReactのコンポーネントを継承している点と、render
メソッドでHTML要素を返す点です。
ClassコンポーネントはReactの登場初期から使われている書き方ですが、最近では次に紹介する関数コンポーネントで記述されることが多いです。
class HelloMessageClass extends React.Component {
render() {
return <div>Hello redimpulz</div>;
}
}
関数コンポーネント
関数コンポーネントはJavaScriptのアロー関数式を用いた記法です。HTML要素を返す関数を定義するだけのシンプルな記述方法です。
関数コンポーネントは新しい書き方で、この後紹介するHooksとも相性が良いので積極的に利用しましょう。
const HelloMessageFunction = () => <div>Hello redimpulz</div>;
アロー関数の書き方として、return
を用いての記述もできます。
const HelloMessageFunction = () => {
return <div>Hello redimpulz</div>;
};
コンポーネントを使用するメリット
コンポーネントは「再利用可能な部品」という紹介をしました。これは、コンポーネントを一度定義したら何度でも呼び出せるということです。例えば次のように<HelloMessageFunction />
をいくつも書くと、「Hello redimpulz」という文字をいくつも表示させることができます。
export default function App() {
return (
<div>
<HelloMessageClass />
<HelloMessageFunction />
<HelloMessageFunction />
<HelloMessageFunction />
<HelloMessageFunction />
</div>
);
}
今回は1行の文字だけでしたが、より複雑なアプリケーションを作るにはコンポーネントの再利用は非常に便利です。
また、コンポーネントごとにファイル分割をするとコードの管理がしやすくなります。ファイルを分割する際には、JavaScriptのモジュール機能を使用し、コンポーネントを定義したファイルでは次のようにexport文を記述します。
export default HelloMessageFunction;
コンポーネントを読み込んで利用するファイルではimport
文を記述します。
import HelloMessageFunction from './HelloMessageFunction';
Props
Propsとはコンポーネントに動的に値を渡す仕組みです。Propsを利用したコンポーネントの例を示します。この例ではPropsを引数props
として受け取ります。
const HelloMessage = (props) => <div>Hello {props.name}</div>;
このコンポーネントは次のように利用できます。
<HelloMessage name='redimpulz'>
name
に文字列を渡している点に注目してください。コンポーネントの引数props
として受け取り、props.name
によって取り出せます。結果として「Hello redimpulz」と表示されます。
次の例はname
を変えた複数のコンポーネントです。
<HelloMessage name='redimpulz1'>
<HelloMessage name='redimpulz2'>
<HelloMessage name='redimpulz3'>
このように、Propsを用いると同じようなコンポーネントを複数作る必要がなくなり、1つのコンポーネントに共通化できます。
propsに渡した値によって条件分岐したり、propsに関数を渡すといった利用ができます。
React Hooks
Reactにはフックと呼ばれる機能があります。React Hooksを使うことにより、関数コンポーネントで様々なReactの機能を利用できます。ここでは、useState(ステートフック)とuseEffect(副作用フック)を紹介します。
useState (ステートフック)
プラスとマイナスをクリックすると数値が変化するカウントアプリを例に考えます。動作の様子は動画の16:16で紹介しています。
この動作は、ボタンを押した際にコンポーネントがもつ値を更新し、画面に反映するという流れで実現されます。
例を見てみましょう。
const [count, setCount] = React.useState(0);
useState
メソッドの引数には初期値を指定します。このメソッドは2つの値を配列で返すので、それぞれをcount
、setCount
という変数名の変数へ代入します。2つ目の変数名には"set"をつけるのが一般的です。
1つ目の変数count
からは現在の値を取得でき、2つ目の変数setCount
に代入されるメソッドを用いて値を更新できます。値を増減するボタンは次のように実装できます。
<button onClick={() => setCount(count + 1)}>
<button onClick={() => setCount(count - 1)}>
アプリケーションはさまざまな状態を保持することが多いです。状態の保持のためにuseStateはとても重要な機能です。
useEffect (副作用フック)
useEffectはコンポーネントでの副作用を指定します。「副作用」という概念を例を通して学んでいきましょう。
次のコードは、コンポーネントが画面に表示されたタイミングで「mounted」と表示する例です。画面に表示される処理のことを「マウント」と言います。
マウント時に処理を行うには、useEffectに関数と空配列を渡します。この関数を副作用といいます。
React.useEffect(() => {
alert('mounted');
}, [])
マウント時に副作用として呼び出す処理としては、初期化処理が挙げられます。例えば、外部からデータを取得して表示するようなアプリケーションでは、マウント時に通信をしてデータを取得します。
前の例では第2引数には空配列を渡していました。この配列内には依存する変数を指定でき、マウント時に加えて変数が変更されるたびに副作用フックが呼ばれるようになります。
次の例では、マウント時とカウントアプリの増減ボタンが押された際に「counted」と表示します。
React.useEffect(() => {
alert('counted');
}, [count])
依存の変更時の副作用フックは、Stateの変更に伴う処理で使われます。例えば、カウントが一定の値を超えた際に文字を表示するといった場合です。
Reactでアプリを実装
これまでに学んだ内容を利用して、Reactでアプリを実装してみます。
今回は、ページアクセス時にAPIからデータを取得し、結果のURLから犬の画像を画面に表示するアプリを作成します。ボタンをクリックして再取得する機能も実装します。
動作の様子は動画の26:03で紹介しています。
まず、画像のURLという状態を管理するため、useState
を用います。
const [imageUrl, setImageUrl] = React.useState("");
次に、画像を取得する関数を実装します。データの取得にはFetch APIを利用します。得られたJSONからURLを取り出し、setImageURL
を用いて更新します。
const fetchData = async () => {
try {
const url = "https://dog.ceo/api/breeds/image/random";
const res = await fetch(url);
const data = await res.json();
setImageUrl(data.message);
} catch (error) {
console.log(error);
}
};
このfetchData
関数は、最初に1度だけ呼び出されるようにしたいです。そこでマウント時に実行されるようにuseEffect
による副作用フックを用います。
React.useEffect(() => {
fetchData();
}, []);
画像の表示は次のようにします。&&
で結ぶことにより、左辺のimageUrl
が空文字でなければ右辺の<img>
を表示するように指定します。
imageUrl && <img src={imageUrl} />
再取得するボタンと、非表示にするボタンを実装します。再取得の方はfetchData
関数を呼び出すことで、非表示にする方はURLを空文字に戻すことで実現できます。
<button onClick={fetchData}>fetch</button>
<button onClick={() => setImageUrl("")}>clear</button>
実装したアプリの改善
アプリを改善するために、次の追加機能を実装します。
- 複数の画像表示に対応
- 初期の取得画像の数をpropsで設定できるように
- 取得する画像の数を入力で変更できるように
- loadingの表示
これらを実現するために、まずuseStateで管理する状態を増やします。
imageNums
は画像の数を、loading
はロード中であればtrueとなるような状態です。また、画像URLも配列でもつように変更します。
const [imageNums, setImageNums] = React.useState(defaultImageNums);
const [imageUrls, setImageUrls] = React.useState([]);
const [loading, setLoading] = React.useState(false);
続いて通信を担うfetchData
を次のように変更します。
まずimageNums
の存在チェックと、1〜50の数値になっていることを確認します。
その後、APIリクエストを行う直前にsetLoading
によりロード中状態に変更します。
APIリクエストの結果を得たら画像URLを更新し、ロード中ではない状態に戻します。
// データ取得とステートの保持
const fetchData = async () => {
if (!imageNums || !(imageNums >= 1 && imageNums <= 50)) {
alert("please select 1...50 number");
return;
}
setLoading(true);
try {
const url = `https://dog.ceo/api/breeds/image/random/${imageNums}`;
const res = await fetch(url);
const data = await res.json();
setImageUrls(data.message);
} catch (error) {
console.log(error);
}
setLoading(false);
};
レンダリングする要素も改良します。ロード中かどうかを状態管理しているので、状態に合わせて表示内容を変えます。loading
がtrueであれば、「loading...」と表示させます。
ロード中でなければ、URLの配列をmapメソッドを用いて、1つずつimg
要素に変換して表示させます。
Reactで配列に格納されたデータを画面に表示する際はmap要素がよく用いられます。mapメソッドについては現役エンジニアが教える!実践JavaScript入門 〜リスト処理編〜でも紹介しています。
loading
? "loading...."
: imageUrls.map((x) => (
<img key={x} src={x} alt="" style={{ width: 100, height: 100 }} />
)
input要素の入力内容に変更があった場合に、画像の数に反映させるためのにhandleChange
関数を実装します。input要素のonChange
によって呼び出されるため、イベントから入力値を取り出しsetImageNums
を用いて更新します。
一般的にReactでinput要素の入力内容を受け取る場合も、このような記述になります。
const handleChange = (e) => {
if (e.target.value) {
setImageNums(parseInt(e.target.value));
} else {
setImageNums("");
}
};
このhandleChange
関数をinput要素のonChange
に指定します。
type="number"
、min
、max
はいずれもHTMLの属性です。入力できる値を数値に限定し、その上限と下限を指定しています。
<input
type="number"
value={imageNums}
onChange={handleChange}
max={50}
min={1}
/>
コードの全体像は次のようになります。コンポーネントを使用する側からdefaultImageNums
をPropsとして渡し、imageNumsの初期値として用いています。
import React from "react";
const HelloFetchImages = ({ defaultImageNums }) => {
const [imageNums, setImageNums] = React.useState(defaultImageNums);
const [imageUrls, setImageUrls] = React.useState([]);
const [loading, setLoading] = React.useState(false);
// データ取得とステートの保持
const fetchData = async () => {
if (!imageNums || !(imageNums >= 1 && imageNums <= 50)) {
alert("please select 1...50 number");
return;
}
setLoading(true);
try {
const url = `https://dog.ceo/api/breeds/image/random/${imageNums}`;
const res = await fetch(url);
const data = await res.json();
setImageUrls(data.message);
} catch (error) {
console.log(error);
}
setLoading(false);
};
const handleChange = (e) => {
if (e.target.value) {
setImageNums(parseInt(e.target.value));
} else {
setImageNums("");
}
};
// 副作用フック(マウント時)
React.useEffect(() => {
fetchData();
}, []);
return (
<div>
{loading
? "loading...."
: imageUrls.map((x) => (
<img key={x} src={x} alt="" style={{ width: 100, height: 100 }} />
))}
<div>
<input
type="number"
value={imageNums}
onChange={handleChange}
max={50}
min={1}
/>
<button onClick={fetchData}>fetch</button>
<button onClick={() => setImageUrls([])}>clear</button>
</div>
</div>
);
};
export default function App() {
return (
<div>
<HelloFetchImages defaultImageNums={20} />
</div>
);
}
実装したReactアプリをビルドする
ここまでは、yarn start
によって開発サーバーを起動してきました。開発サーバーでは編集するたびに自動的にリロードする機能を使うことで効率よくコーディングができました。本番環境で使う際には、開発サーバーを使うのではなくビルドという作業をし、ビルドしたファイルをサーバーに配置します。
ビルドは yarn build
コマンドで実行できます。ビルドが完了するとbuild
というディレクトリにファイルが作られます。このディレクトリをサーバーに配置することによってReactアプリが配信できます。
ビルドされたファイルを確認しましょう。ビルド時にヒントとして表示される次のコマンドを実行することで、ビルドしたファイルの動作確認ができます。
npm install -g serve
serve -s build
この先学んでおきたいこと
ここまでの知識でReactアプリケーションを構築できますが、より複雑や大規模なアプリケーション作成に挑戦する際は、以下のようなコンテンツも学習すると良いでしょう。
- ルーティング、ナビゲーション(React Router)
- グローバルステート (useContext, useReducer, Redux)
- 最適化 (useMemo, useCallback)
- DOMへのアクセス (useRef)
- Next.js (本番環境向けのReact開発)
まとめ
今回はReactの基礎を学びました。これらの知識でReactアプリケーションを構築できます。様々なアプリ開発に挑戦してみましょう。