Published: 2021-05-22
Reactフロントエンド開発ことはじめ
近年のクライアントサイドJavaScriptはもはや単なるスクリプトではなく、より構造化されたプログラミングが求められるようになっています。 フロントエンドのフレームワークが作られていますが、今回はその一つであるReactについてまとめます。
Reactを導入するメリット
ランディングページやキャンペーンサイトなどの少ないWebサイトと異なり、 SNSやWebサービスなどのWebアプリケーションでは、大量の情報を扱ったり、多様な条件による表示の出し分けなどが必要になります。 Reactは次のようなメリットがあります。
- コンポーネント化して再利用
- XMLライクで視覚的なJSX記法
- データの「状態」と「表示」を分離
ひとつつずつ解説します。
コンポーネント化による再利用
一般的に、Webアプリはボタン、メニュー、テキストなど多くの要素で構成されます。 このそれぞれの要素をそれぞれ組み立て可能な部品に見立てたものをコンポーネントと考えます。
例えば、誰かのコメントの投稿は、その人のユーザ名、コメント本文、投稿日時などといった要素をひとまとまりと考えることができます。 これをコンポーネントと呼ばれる部品やパーツのようなものに見立て、繰り返し利用するのです。
import React from 'react'
//コメント1件に相当する、Commentコンポーネント
const Comment = (props) => {
return (
<li className="article">
<h3>{props.userName}</h3>
<p>
{props.comment}
<span>{props.postedTime}</span>
</p>
</li>
)
}
export default Comment
上記のCommentがコンポーネントを関数コンポーネントとして書いたものです。
XMLライクで視覚的なJSX記法
コンポーネントで要素を表現する際、JSXという記法を用います。 XMLタグのように表現することができ、コンポーネントの構造を視覚的に記述することができます。 JavaScriptによるレンダリングの制御もできます。
Article.jsimport React from 'react'
import Title from './components/Title'
import Body from './components/Body'
import Comment from './components/Comment'
const Article = () => {
const comments = []; //コメントの配列
return (
<div className="article">
<Title />
<Body />
{ comments.legth > 0 ?
comments.map((e, index) => {
<Comment key={index} data={e} />
}) : '' }
</li>
)
}
export default Article
XMLライクで視覚的なJSX記法
ブラウザ上でデータを扱う場合、そのデータと表示の整合性を保つことが必要ですが、 そのために、常に気をかけなければなりません。Reactを利用すれば、データの状態変化を検知して自動的に画面レンダリングし直してくれる上、 なるべく必要なDOM要素だけを変更するので、処理が早くキビキビと見えます。
Article.jsimport React, {useState} from 'react'
const LikeButton = () => {
//likeという変数の状態を保持(初期値0)
//setLike()メソッドでlikeの値を更新する
const [like, setLike] = useState(0)
//ボタンを押したときのイベント
const handleLikeButton = () => {
setLike(like + 1) //likeの値に1を足した値で更新
}
return (
<button onClick={handleLikeButton}>{like}</button>
)
}
export default LikeButton
useState()はReact16.8から導入されたReact Hooksによる状態管理のためのメソッドです。
React開発環境構築
Reactの導入
新規プロジェクト用のディレクトリを作成し、npmパッケージをインストールします。 index.htmlと、Reactを記述するindex.jsを作成します。 index.html内で読み込むbundle.jsは後述するWebpackでindex.jsから変換したものを指定しています。
consolenpm init -f
npm install react react-dom
index.jsimport React from 'react'
import ReactDOM from 'react-dom'
const Welcome = props => {
return <h1>Hello, {props.name}</h1>
}
ReactDOM.render(
<Welcome name="Johnny" />,
document.getElementById('root')
)
index.html...
<body>
<div id="root"></div>
<script src="bundle.js"></script>
</body>
Webpackの導入
Reactではコンポーネントという単位のJSファイルと多数のパッケージを扱いますが、これを組み合わせてアプリケーションとして動作させます。 また、他のパッケージとこれらの依存関係を解決して、たった一枚のJSファイルにまとめてくれる(ビルドしてくれる)npmパッケージがWebpackです。 モジュールバンドルツールと呼ぶらしい。
consolenpm install -g webpack webpack-cli
webpack.config.jsmodule.exports = {
entry: './index.js',
output: {
filename: 'bundle.js'
}
}
Babelの導入
また、ビルドする際にBabelを適用します。ReactやTypeScriptなどの拡張JSをブラウザが認識できるJavaScriptに変換させるようにします。 IEなどのブラウザへの後方互換性をもたせます。
consolenpm install --save-dev babel-loader babel-core
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react
rulesの中に、変換対象のファイル(test)、使うローダー(loader)、オプション(options)を指定します。 以下の場合は、.js拡張子のファイルについて、Babelでes2015とreactを変換するということになります。
webpack.config.jsmodule.exports = {
mode : 'development',
entry: './index.js',
output: {
publicPath: '/dist/',
filename: 'bundle.js'
},
module: {
rules: [
{
test: /.js$/,
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react',
],
}
}
]
}
}
webpackコマンドでReact+ES2015で記述したindex.jsから、JavaScriptへの変換したdist/bundle.jsが生成できます。
consolewebpack
webpack-dev-serverの導入
Reactアプリケーションをローカルのブラウザ上で確認しながら開発できます。
変更を検知してリロードしてくれるなど便利ですが、更新スピードが早くバージョンアップによる破壊的な変更も多いです。 ドキュメントを参考に設定してください。 以下はバージョン3系による設定です。
npm install —-save-dev webpack-dev-server
webpack.config.jsmodule.exports = {
module: {
...
},
devServer: {
contentBase: __dirname,
compress: true,
port: 3333,
inline: true,
watchOptions: {
ignored: /node_modules/
}
},
...
}
consolewebpack serve --open
ローカルサーバが起動し、ブラウザが立ち上がります(--openオプション)。
React関数コンポーネント開発
コンポーネント間のやり取り
一般的にコンポーネントには親子関係があります。 より大きいコンポーネントが親、その親に含まれるコンポーネントが子となります。 コンポーネントはそのコンポーネントが保持すべき状態(state)を持っており、 親子間でその状態をあらわすデータをpropsまたはイベントを通して受け渡します。
親とstate
コンポーネントは状態(state)を保てます。
Parent.jsimport React, {useState} from 'react'
import Child from './Child'
const Parent = () => {
const [state, setState] = useState({
firstName: "John",
lastName: "Smith"
})
return <Child lastName={state.lastName} />
}
親から子へ値(props)を渡す
親コンポーネントから子コンポーネントへは、propsを通して値を渡します。
Child.jsimport React from 'react'
const Child = props => {
return (
<div>Hello, Anthony {props.lastName}.</div>
)
}
export default Child
子から親へイベントを渡す
ボタンが押されたことを通知するときや、テキスト入力があった場合など、 子コンポーネントから親コンポートへは、イベントを通して値を渡します。 ここでは、子コンポーネントのテキストを入力したら、親コンポーネントのstateを変更するようなイベントを作成しています。
Child.jsimport React from 'react'
const Child = props => {
//入力に変更があった場合のイベント
function handleChange(e) {
//親からpropsとして渡されたイベントを呼ぶ
props.onChangeFirstName(e.target.value);
}
return (
<div>
<div>Hello, {props.name.firstName} {props.name.lastName}</div>
<input
type="text"
placeholder="firstName"
onChange={handleChange}
/>
</div>
);
}
export default Child
Parent.jsimport React from 'react'
import Child from './Child'
const Parent = () => {
const [state, setState] = useState({
firstName: "John",
lastName: "Smith"
})
const handleChangeFirstName = name => {
setState({
...state,
firstName: name
})
}
return (
<Child
name={state}
onChangeFirstName={handleChangeFirstName} //onChangeFirstNameイベントハンドラを渡す
/>
)
}
ステップアップ
非同期通信(axios)とasync/await
非同期通信を行うパッケージにaxiosがあります。
consolenpm install --save-dev axios
また、Reactで非同期通信を使用する場合は以下のパッケージが必要になります。
consolenpm install --save-dev @babel/polyfill
また、Reactで非同期通信を使用する場合は以下のパッケージが必要になります。
webpack.config.jsmodule.exports = {
mode : 'development',
entry: ['@babel/polyfill', './src/index.js'], // @babel/polyfillを追記
...
Reactに限らずJavaScriptのプロセスでは、非同期通信の結果が来るのを待たずに次の処理に進んでしまいます。 ところが非同期通信の結果を待って処理を進めたい場合は、async/awaitを使うことで平易に書けます。
- async
- この処理が非同期の関数であることを表す。
"async function SomeFunc() {...}" や "const someFunc = async () => {...}" などのように関数定義の前に書く。 - await
- この処理の結果が返ってくるまでは次の処理に進まない。なお、awaitさせる処理を含む関数にはasyncをつける。
import axios from 'axios'
async function componentDidMount {
const response = await axios.get('https://example.com/')
.then(response => {
return response.data;
});
return response;
}
Styled Componentの導入
React自体にはCSSなどのスタイルは持たないので、利用者が独自にCSSで装飾を加える必要があります。 Reactにおけるスタイリングの方法としてStyled Componentsというものがあり、コンポーネントのjsファイル内にCSS(Sassも可)を書くことができます。 これにより、CSSも含めたコンポーネントを作成できるので、コンポーネントの依存性が少なくなります。
Reduxの導入
フロントエンドで扱う情報量が多くなってくると、その状態を管理するのが煩雑になってきます。 ReduxではデータをStoreと呼ばれる場所に一元的においておくことで、データの変更の流れの見通しを良くすることができます。 Reduxについては別記事で扱います。
Reduxでフロントで扱うデータを一元管理するcreate-react-appについて
Reactの環境構築はやや手順が多く面倒ですが、手っ取り早くReactの環境を始めたい場合は、 create-react-appパッケージを使うとすぐに導入できます。
consolenpm install -g create-react-app
create-react-app my_app