はじめに
以下の知識の習得を目的として行った学習メモになります。
- Reactの基礎知識
- JSXについて
- create-react-appでの環境構築
- Reactコンポーネントの作り方
- モジュールのimport、exportについて
- React Hooksについて
Reactの基礎知識
Reactとは?
- Facebookが開発
- JavaScriptのライブラリ(フレームワークではない)
- WebのUIを作る
- React ≠ SPA
コンポーネントとは?
UIは2つに分類される
1. 見た目(View)
2. 機能(Controller)
コンポーネント = 見た目 + 機能
Webページはコンポーネントのツリー構造になっている
なぜコンポーネントを使うのか
- 再利用性するため
- 分割統治するため
- 変更に強くするため
Virtual DOM
そもそもDOMとは?
→ Document Object Modelの略
→ インターフェース
→ HTMLにアクセスする窓口
→ HTML構造、見た目、コンテンツを変更したいときはDOMを通して操作を行う
Virtual DOMとは?
Reactで管理するDOM。
通常のDOMはブラウザのレンダリングによって管理されるが
Reactではブラウザのレンダリングと別で管理を行う
→効率よくDOM操作できる
通常のDOM操作
document.getElementById('hoge').innerText='fuga';
ReactのVirtual DOM操作
render(
<div id='hoge'>fuga</div>
);
差分描画
Reactでは変更されたVirtual DOMの差分のみを再描画する
JSX
JavaScript内でHTMLっぽく書ける
ReactDOM.render(
<div className={hoge}>
<h1>Hello World!</h1>
</div>
)
JSXの基礎知識と文法
JSXとは?
- JavaScript内でHTMLを簡単に記述するための言語
- JavaScriptの拡張言語
- Facebookが開発
- React公式ドキュメントはほぼJSXで記述されている
- Reactでは業界標準
なぜJSXを使う?
通常のJavaScriptでHTMLを記述(DOM操作)
const fuga = "<h1>Hello, World!</h1>"
document.getElementById('hoge').innerHTML = fuga;
量が増えると。。
const fuga = "<h1>Hello, World!</h1>"
const foo = "<h2>React Commentary</h2>"
const bar = "Hi, I'm Billy Gibbons."
document.getElementById('hoge').innerHTML = fuga;
document.getElementById('foo').innerHTML = foo;
document.getElementById('bar').innerHTML = bar;
JSXを使うと。。
return (
<React.fragment>
<div id="hoge">
<h1>Hello, World!</h1>
</div>
<div id="foo">
<h2>React Commentary</h2>
</div>
<p id="foo">Hi, I'm Billy Gibbons.</p>
</React.fragment>
)
可読性が高い!
ただJSXは実際のところJavaScriptではない。
JSXの構文をブラウザは理解できない。
そこでトランスパイラが必要。
トランスパイラ
「翻訳」のような役割。
JSX → JavaScript(ES6) → JavaScript(ES5)
ReactのトランスパイラはBabel
トランスパイラを主たる実装として開発されている言語の例
CoffeeScript、TypeScript...etc
もしJSXがなかったら。。
React.createElementを使う
React.createElement(
"h1",
null,
"Hello, World!"
)
JSXを使用して記述したJSをBabelでトランスパイルするとReact.createElementを使用した形に変換される
JSXの基本文法
1.Reactパッケージのインストールが必要
// .jsxファイル内の先頭に宣言
import React from "react";
2.HTMLとほぼ同じ文法(ただclass
はclassName
に)
const App = () => {
return (
<div id="hoge" className="fuga">
<h1>Hello, World!</h1>
</div>
);
};
3.{}
内に変数や関数を埋め込める
const foo = "<h1>Hello, World!</h1>"
const App = () => {
return (
<div id="hoge" className="fuga">
{foo}
</div>
);
};
4.変数名などは全てキャメルケースで記述する
const fooBar = "<h1>Hello, World!</h1>"
const App = () => {
return (
<div id="hoge" className="fuga">
{fooBar}
</div>
);
};
5.空要素は閉じる
const App = () => {
return (
<div id="hoge" className="fuga">
<input type="text" id="blankElement" />
<img src="/assets/icon/icon.png" />
</div>
);
};
環境構築
create-react-app
必要なもの
- node 8.10以上
- npm 5.6以上
上記インストールのためにhomebrew、nodebrewが必要
homebrewのインストール
nodebrewのインストール
$ brew install nodebrew
$ nodebrew -v // インストールの確認
nodeのインストール
参考:https://qiita.com/kyosuke5_20/items/c5f68fc9d89b84c0df09
$ nodebrew ls-remote // インストール可能なnodeのバージョン確認
$ nodebrew install stable // 安定版のインストール
$ nodebrew ls // 現在インストールされているnodeのバージョン一覧
$ nodebrew use v{インストールしたバージョン} // currentへの追加
$ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.zprofile // zshの場合
npmのインストール
nodeを入れた時点でnpmも入る
バージョンが5.6以上であることを確認する
npm -v
create-react-appによるプロジェクトの作成
$ npx create-react-app react-blog-app
create-react-appとは
React開発環境を超簡単に構築できるツール。
Reactを学習するのに最適な環境
(React公式documentから引用)
- React開発環境の構築は難しい
- トランスパイラのbabelやバンドラーのwebpackの設定が必要
create-react-appなら1コマンドで環境を整えてくれる
create-react-appの環境構成
-
src
: コンポーネントを作るJSファイルなど -
public
: htmlファイルや設定ファイルなど。manifest.json
はPWAを開発する際に使用する設定ファイル -
build
: 本番環境用のファイル
基本コマンド
$ npm run build
srcとpublic内のファイルを1つにまとめて(バンドル)、buildディレクトリに出力する
$ npm start
ローカルサーバを起動してReactアプリを動かす
$ npm run eject
babelやwebpackの設定を変更したい時に使用する
その他の環境構築ツール
- Next.js → サーバーサイドレンダリング(SSR)
- Gatsby → 静的ウェブサイトに最適(SSG)
コンポーネントの基本
コンポーネントとは?
UIは2つに分類される
1. 見た目(View)
2. 機能(Controller)
コンポーネント = 見た目 + 機能
Webページはコンポーネントのツリー構造になっている
なぜコンポーネントを使うのか
- 再利用性するため
- 分割統治するため
- 変更に強くするため
コンポーネントの種類
-
Class Component
: クラスによって定義されたコンポーネント -
Functional Component
: 関数型で定義されたコンポーネント
Functional Component
- ES6のアロー関数で記述
- stateを持たない(stateless)
- propsを引数に受け取る
- JSXをreturnする
- シンプル
import React from 'react';
const Article = (props) => {
return (
<div>
<h2>{props.title}</h2>
</div>
);
};
export default Article;
Class Component
- React.Componentを継承
- ライフサイクルやstateを持つ
- propsにはthisが必要
- renderメソッド内でJSXをreturnする
import React from 'react';
class Article extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h2>{this.props.title}</h2>
</div>
);
}
}
export default Article;
最近の主流はFunctional Component
- 記述量が少ない
- コンポーネントにstateを持たせたくない
propsでデータを受け渡す
親コンポーネント
import React from 'react';
import Article from "./Article";
const Blog = () => {
return (
<React.Fragment>
<Article title={'Hello, React'}/>
</React.Fragment>
);
}
export default Blog;
子コンポーネント
import React from 'react';
const Article = (props) => {
return (
<div>
<h2>{props.title}</h2>
</div>
);
};
export default Article;
受け渡せるデータ型
-
{}
内に記述 - 文字列、数値、真偽値、配列、オブジェクトなどなんでも渡せる
- 文字列は
{}
なくてもOK
import React from 'react';
import Article from "./Article";
const Blog = () => {
const authorName = 'Eric Clapton';
return (
<React.Fragment>
<Article
title={'Hello, React.'}
order={3}
isPublished={true}
author={authorName}
/>
</React.Fragment>
);
}
export default Blog;
再利用する
コンポーネントは再利用できることが最大の利点
import React from 'react';
import Article from "./Article";
const Blog = () => {
const authorName = 'Eric Clapton';
return (
<React.Fragment>
<Article title={'出生について'}/>
<Article title={'Cream時代'}/>
<Article title={'ソロ活動時代'}/>
</React.Fragment>
);
}
export default Blog;
コンポーネントの状態
stateの設定と取得と変更
状態(state)とは
- コンポーネントの中で管理する変数
- ローカルステートと呼ばれる
- propsとして子コンポーネントに渡せる
なぜstateを使うのか
- render()内では値を変更してはいけない
- setState()で値を変更する
- stateの変更 = 再レンダーのきっかけ
→ページをリロードせずに表示を切り替えられる
stateの設定方法
- Class Componentが前提
- constructor()内で宣言
- オブジェクト型で記述
import React from 'react';
import Article from "./Article";
class Blog extends React.Component {
constructor(props) {
super(props);
this.state = {
isPublished: false
}
}
// 略
}
export default Blog;
stateの取得
- 同コンポーネント内なら
this.state.key名
で取得できる - 子コンポーネントで参照したい場合はpropsとして渡す
render() {
return (
<React.Fragment>
<Article
title={'出生について'}
isPublished={this.state.isPublished}
/>
</React.Fragment>
);
}
stateの変更方法
- setState()を使う
- 関数にラップするのが一般的
- setState()内に記述されたstateのみを変更
// 公開状態を反転させる関数を定義する
togglePublished = () => {
this.setState({
isPublished: !this.state.isPublished
});
}
コンポーネントのライフサイクル
ライフサイクルとは
- コンポーネントの「時間の流れ」
- 生成、変更、破棄までの循環
- それぞれの段階で必要な処理を記述
3種類のライフサイクル
Mounting
:コンポーネントが生成される期間
Updating
:コンポーネントが変更される期間
Unmounting
:コンポーネントが破棄される期間
なぜライフサイクルを使うのか
-
関数外に影響を与える関数
を記述するため
e.g. DOM変更、API通信、ログ出力、setState() ...etc - 副作用 = 適切な場所に配置すべき処理
order | Mounting | Updating | Unmounting |
---|---|---|---|
1 | constructor() | ||
2 | render() | render() | |
3 | componentDidMount() | ||
4 | componentDidUpdate() | ||
5 | componentWillUnmount() |
主要メソッド
Mount
constructor()
:初期化(stateなど)
render()
:VDOMを描画(JSXをreturn)
componentDidMount()
:render()後に一度だけ呼ばれる。リスナーの設定やAPI通信に使われる。
Updating
render()
:VDOMを描画(JSXをreturn)
componentDidUpdate()
:再render()後に呼ばれる。スクロールイベントや条件付きイベント。
Unmounting
componentWillUnmount()
:コンポーネントが破棄される直前に呼ばれる。リソースを解放するために使う。(リスナーの解除など)
importとexport
モジュール化について
- 他言語では昔からある概念
- JavaScriptではES2015(ES6)から採用
- 基本的に1ファイル=1モジュール
- 任意の場所で読み込んで使用できる
モジュール化のメリット
- 分割統治できる
大規模プログラムでも管理しやすくなる - 任意の場所で読み込める
必要なものを必要な分だけ - 再利用できる
何度も同じコードを書かなくていい
名前付きexport
1モジュールから複数の関数をexport
クラスはexportできない
export function Foo() {
return (
<h1>FOO</h1>
);
}
export const Bar = () => {
return (
<h1>BAR</h1>
);
}
名前なし(default)export
- 1ファイル(1モジュール) 1export
- ES6で推奨されているexport方法
- アロー関数は宣言後にexport
- クラスをexportできる
export default function Foo() {
return (
<h1>FOO</h1>
);
}
const Bar = () => {
return (
<h1>BAR</h1>
);
}
export default Bar;
export default class Hoge extends Fuga {
render() {
return (
<h1>Hoge</h1>
);
}
}
モジュール全体のimport
- 名前なし(default)exportしたモジュールをimportする
- モジュール全体のimport
import React from 'react';
import Article from "./Article";
const Article = (props) => {
return (
<div>Article</div>
);
}
export default Article;
関数ごとのimport
- 名前付きexportされたモジュールをimportする
- {}内にimportしたい関数名
import { Foo, Bar } from "./FooBar";
export function Foo() {
return (
<h1>FOO</h1>
);
}
export const Bar = () => {
return (
<h1>BAR</h1>
);
}
別名import
- 別名(エイリアス)をつけてimportできる
- モジュール全体なら
* as name
- モジュール一部なら
A as B
import React from 'react';
import * as AnotherName from './Article';
import { Foo as MyFoo } from './FooBar';
const Article = (props) => {
return (
<div>Article</div>
);
}
export default Article;
React Hooks
関数コンポーネントでもstateを扱う
Hookとは
- クラスの機能(stateやライフサイクル)を
Functional Component
でも使える機能 - React 16.8から導入された(2020/2に正式リリース)
- 100%後方互換
→古い書き方をしているコンポーネントなどに影響を及ぼさない
→小さく導入できる
なぜHookを使うのか
シンプルさを保つため
-
Class Component
は複雑になりやすい-
this
という悪魔 - stateを扱うロジックが複雑
- 複数のライフサイクルメソッドに副作用のある処理がまたがる
-
useState()
を使う
- ステートフックと呼ばれる
- クラスコンポーネントでいう
this.state
とthis.setState()
の代替 - 複数のstateを扱うときはstate毎に宣言する
1.useState関数をインポート
import React, {useState} from 'react';
2.宣言する
const [isPublished, togglePublished] = useState(false);
3.JSX内で使う
<input /略/ onClick={() => togglePublished(!isPublished)} />
Functional Componentでもライフサイクルを扱う
useEffect()
を利用する
useEffect()
のメリット
- ライフサイクルメソッドを代替できる
- Functional Componentでライフサイクルを扱える
- コードをまとめることができる
- 🙆♀️ 機能ベース(何をやっているのか)
- 🙅♀️ 時の流れベース(ライフサイクルのメソッド毎)
useEffect()
の仕組み
- レンダー毎に
useEffect()
内の処理が走る - 代替できるメソッド
componentDidMount()
componentDidUpdate()
componentWillUnmount()
useEffect()
の使用
パターン① レンダー毎
useEffect(() => {
console.log('Render!');
return () => {
console.log('Unmounting!');
}
})
- 基本形
-
useEffect()
内にCallback関数を書く - Callbackはレンダー毎に呼ばれる
- returnするCallback関数はアンマウント時に呼ばれる。(クリーンアップ関数)
パターン② マウント時のみ実行
useEffect(() => {
console.log('Render!');
}, [])
- 第二引数の配列内の値を前回レンダーと今回レンダーで比較
- 変更があればCallback関数を実行
- 第二引数に空の配列を渡すと最初の1回(マウント時)のみ実行される
パターン③ マウント&アンマウント時に実行
useEffect(() => {
console.log('Render!');
return () => {
console.log('Unmounting!');
}
}, [])
- ①と②の複合形
- 通常のCallbackはマウント時のみ
- 配列が空の場合はUpdating期間は処理が実行されない
- アンマウント時はretrun内のクリーンアップ関数が実行される
パターン④ 特定のレンダー時に実行
const [limit, release] = useState(true);
useEffect(() => {
console.log('Render!');
}, [limit])
- マウント時に実行される
- limitの値が変わった時に実行される