よく使い方を忘れるので、必要な時にサッと読み返せるように超完結にまとめておきます。
勉強中の関連記事まとめ
自分用の勉強記事をまとめた目次 ~擬似知識体系~
#ロードマップ|前提スキル・環境構築・目標設定・拡張方針・用語リスト
#React.jsとは
ViewレイヤのJavaScriptのフロントエンドのフレームワーク
###宣言的View
データが変更されたコンポーネント(要素)だけ更新してレンダリングする
レンダリング結果を想像しやすいJSX記法が利用可能
###コンポーネントベース
コンポーネントとは、UIを独立した再利用できるテンプレート部品に分割してそれぞれを分離して考えることができる概念。
HTMLタグが組み込まれた複雑なデータを内包するコンポーネントを、JavaScriptの記述で呼び出したりアプリに渡すことができ、DOMの外側で状態を保持できる。独自のタグを作る機能。コンポーネント名の頭文字は大文字にする(例 <Layout>
, <Link>
など)。コンポーネントの定義はfunction
で行い、要素の中身をreturn
で返す。コンポーネントによってレイアウトをモジュール化することでWEBアプリのUI作成を効率化できる。ReactNativeを使えばiOSアプリやAndroidアプリにも対応できる。
function コンポーネント名() {
return <div>要素の中身</div>
}
###拡張性
#どんなものが作れる?
###簡単なUIを作ってみる
まずはイベントの設定。JSXで記述したhtmlタグの中にイベント名として上で定義したクラスの中のメソッドを渡す。
// コンポーネントの中身
<button イベント名={() => {処理}}>ボタンのラベル</button>
<button onClick={() => {}}>ボタンのラベル</button>
###レイアウトテンプレート
###タイマー表示
###ToDoアプリ
###外部プラグインの導入
#【勉強】基礎知識の把握
###ファイル構造
MyApp.js
のreturnの中身がMyApp
という名前でモジュール化される。
// MyApp.js
class MyApp extends ReactConponent {
render() {
return( // return()の中身が...
<div>
<h1>タイトル</h1>
</div>
);
}
}
export default MyApp; // MyAppという名前でモジュール化され...
index.js
のRezctDOM.render()
でMyApp
の中身を取得し、id="root"
の要素の中身に書き出す。
import MyApp from './components'MyApp; // MyAppモジュールを読み込み...
ReactDOM.render(<MyApp/>, document.getElementById('root')); // render()の中身を取得してrootに渡し...
<div id="root">
の中にMyApp
が表示される。
<body>
<div id="root"> <!-- rootの中身挿入される -->
</div>
</body>
###全体の基本構文
import React from 'react'; // Reactをインポート
class ClassName extends ReactComponent{ // React.Componentを継承するクラス定義
const text = 'render()の外は普通にjsとして書く';
render(){ // JSXでコンポーネントの要素の中身を記述
// ここに処理内容を記述(JSX記法はrenderの中だけ)
<p>{text}</p> /// return()の中でjsを読み込むときは{}で囲む
}
}
export default ClassName; // クラスをエクスポートしてメソッドを呼び出せるようにしておく
###普通にcssを適用
<link rel="stylesheet" href="style.css">
h1{
color:pink;
}
p{
color:skyblue;
}
// MyApp.js
return(
<div>
<h1>タイトル</h1>
<p>テキスト</p>
</div>
);
###classNameでcssを適用
<link rel="stylesheet" href="style.css">
.title{
color:pink;
}
.text{
color:skyblue;
}
// MyApp.js
return(
<div>
<h1 className='title'>タイトル</h1>
<p className='text'>テキスト</p>
</div>
);
###JSX
クラス定義の中で宣言したrender(){}
メソッドのreturn();
でコンポーネントの要素の中身をJSXで記述する。JSX記法は、JavaScriptコード内にhtmlタグなどを記述でき、一つの親要素でまとめておけば子要素を複数書くことができる。
class ClassName extends ReactComponent{
render() {
return(
<div>
<h1>コンポーネントの中身を書ける</h1>
{/* コメントアウトはカッコが必要 */}
<ul>
<li>一つの親要素で</li>
<li>囲っておけば</li>
<li>複数要素を書ける</li>
</ul>
<img src='閉じないタグは最後にスラッシュが必要'/>
</div>
);
}
}
###render()メソッド
コンポーネントをレンダリングするメソッド。return
で返り値としてレンダリングする要素を返す。
// MyApp.js
render() {
return();
}
#クラスの理解|import・export(ファイル分割とデータの流れ)
###export default ComponentName;
エクスポートの意味は、そのjsファイルの中で定義したコンポーネントに名前をつけてテンプレートファイルとして呼び出せるようにすること。エクスポートしたコンポーネントを他のファイルでインポートしてrender()
の中で<ComponentName/>
のように独自のタグを記述すればhtmlにレンダリングできる。
// Layout.js
export default Layout;
#コンストラクタの理解|this・インスタンス
###thisの中身はWindowオブジェクト
console.log(this);
Window {stop: function, open: function, alert: function…}
this.sample = 'サンプル';
サンプル
###関数の中のthisはWindowオブジェクトを指す
function func(){
console.log(this);
}
func();
Window {stop: function, open: function, alert: function…}
###オブジェクトの中のthisはオブジェクト自身を指す
var obj = {
name:'山田',
func:function(){
console.log(this);
}
}
obj.func();
Object {name: "山田", func: function}
###コンストラクタの中のthisはコンストラクタ自身を指す
function User(name,age){ // {name:name, age,age}というインスタンスを生成するコンストラクタの定義
this.name = name;
this.age = age;
}
let yamada = new User('山田',85); // 引数を渡してインスタンスを生成
console.log(yamada);
User {name:'山田', age:85}
###コンポーネントのthisはコンストラクタのthis
コンストラクタとは、インスタンス(任意のプロパティを持つオブジェクト)を生成する関数もしくはメソッドのこと。React.jsのコンポーネントとはまさにインスタンスを独自タグで呼び出して使い回す仕様。つまりコンポーネント定義の際に記述するthis.props.name
を分かりやすく言語化すると「親コンポーネントのコンストラクタの中で(this)定義されたひな型のオブジェクトの(props)nameプロパティの値(name)」と言う意味になり、もう少し短く表現すると「親コンポーネントのコンストラクタの内のどの値か」ということになる。
// User.js
import React from 'react';
class User extends React.Component {
render() {
return (
<div>
<div>{this.props.name}</div> {/* 言語化すると{親コンポーネントであるAppクラスのコンストラクタで定義した.ひな型オブジェクトの.nameプロパティの値}という意味*/}
<img src={this.props.image} />
</div>
);
}
}
export default User;
こっちが親コンポーネントを定義しているApp.js。親コンポーネントの方ではthis.props.name
といった記述ではなく、分かりやすい名前のローカル変数userItem.name
を使っている。
// App.js
import React from 'react';
import User from './User';
class App extends React.Component {
render() {
const userList = [
{ name: '山田', image: 'http...png' },
{ name: '田中', image: 'http...png' },
{ name: '山中', image: 'http...png' },
]
}
return(
<div>
<h1>ユーザリスト</h1>
<div>
{userList.map((userItem) => {
return (
<User name={userItem.name} image={userItem.image} /> // 子コンポーネントである<User/>タグにpropsとして配列userListのオブジェクトのいずれかを当てはめると言う意味
})}
</div>
</div>
);
}
export default App;
#コンポーネントの理解|render()・props・state・戻り値・イベント
- コンポーネント名は頭文字を大文字にすること
###propsとはタグの属性に渡すオブジェクト値
Reactがユーザが定義したコンポーネントを検出すると、JSXでタグ内に記述された属性を一塊りのオブジェクトとして扱う。このオブジェクトのことをpropsと呼ぶ。
const element = <div/>; // これはただの要素
const element = <Welcome name="山田"/>; // これはWelcomeコンポーネントにprops渡したやつ
上記のコードだと、ユーザが定義した<Welcome/>
タグの中のname
属性を、Reactは{name:'山田'}
というオブジェクトとして扱う、ということ。
###a
// Welcomeコンポーネント
function Welcome(props) {
return <h1>こんにちは {props.name}</h1>;
}
const element = <Welcome name="山田" />;
ReactDOM.render(
element,
document.getElementById('root')
);
-
<Welcome name="Sara"/>
という要素を引数として ReactDOM.render()を呼び出します。 - ReactはWelcomeコンポーネントを呼び出し、そのときにprops として{name:'Sara'}を渡します。
- Welcomeコンポーネントは出力として
<h1>Hello, Sara</h1>
要素を返します。 - ReactDOMは
<h1>Hello, Sara</h1>
に一致するよう、DOM を効率的に更新します。
###親コンポーネントと子コンポーネントの使い所
コンポーネントには親子関係があり、コンポーネントの中で他コンポーネントを参照できる。子コンポーネントを包括するのが親コンポーネントで、その中に子コンポーネントのタグを記述し、そのタグの属性にオブジェクト値を渡せば、同じレイアウトで異なるデータを表示できる。
例えば下記のコードでは、複数の<Welcome/>
コンポーネントに個別のユーザ名のオブジェクト値を渡してレンダリングする<App/>
コンポーネントなども作れる。
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Reactでは、ボタン、フォーム、ダイアログ、画面などはコンポーネントとして解釈される。既存のアプリにReactを統合する場合は、<Button/>
のような小さなコンポーネントからボトムアップで始め、徐々にビューの階層構造の頂上に向かって進んでいってもよい。
###コンポーネント化でネストを解消して一括編集を可能にする
下記のコードの例は、SNSなどのコメント欄を実装するもの。このままでは子要素が多すぎてソースが煩わしく、この一部を他のページで再利用したくても不要な要素が多く、そのため変更を加える際には各ページで個々に編集する手間が発生する。
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar" // この部分をコンポーネント化する
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
しかし、例えば<Avatar/>
コンポーネントについてはavatarUrl
とname
というpropsを持っており、このユーザ情報は他でも使いまわせる可能性が高いので、分離してコンポーネントとして管理する方が効率的。そうするためには下記のように、まずAvatar部分を抽出してコンポーネント定義する。
このとき、propsの名前はauthor
ではなくuser
に変更しているが、これは<Avatar/>
コンポーネントは様々なページで使い回す予定なので、<Comment/>
内で使うことはここでは気にしなくても良く、自身のコンポーネントからの観点でpropsの名前を決めた方が管理しやすいため。
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl} // propsの名前をauthorからuserに変更している
alt={props.user.name}
/>
);
}
これによってclassName="Avatar"
の部分を<Avatar/>
に置換できる。
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo"> {/* 次はここをコンポーネント化する*/}
<Avatar user={props.author} /> {/* 1行で済む*/}
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
さらにclassName="UserInfo
の部分を抽出してコンポーネント定義する。
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
するとここまでコードを減らすことができる。
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} /> {/* 1行で済む*/}
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
###propsの渡し方
3パターンある。オブジェクト名.プロパティ名
で渡す方法と、this.props.プロパティ名
で渡す方法と、メソッド名.プロパティ名
で渡す方法。props
はReact.jsによって用意されたデータを管理するための変数。
用語 | 意味 | 詳細 |
---|---|---|
props | 小道具 | 不変データとして扱われ、getDefaultProps で定義されたデフォルト値か、親コンポーネントから渡された値のどちらかを持つ。 |
state | 状態 | 主にUIに関する状態である現在のDOMと変更後のDOMの差分からコンポーネントを際描画するための可変データ。コンポーネントからコンポーネントに渡される。this.setState() をコンポーネント内で呼び出site更新し、既存stateを新規stateで置換してrender()メソッドが実行される。 |
オブジェクト名.プロパティ名
で渡すとき、同じクラス内で定数を定義してオブジェクト値を代入し、その定数名すなわちオブジェクト名とプロパティを指定して値を渡している。
// オブジェクト名.プロパティ名で渡す方法
const userList = {name:'山田', image:'http...png'};
return(
<div>
<p>{userList.name}</p>
<img src={userList.image}/>
</div>
);
this.props.プロパティ名
で渡すとき、コンポーネントとして定義するために
// this.props.プロパティ名で渡す方法
<p>{this.props.name}</p>
<img src={this.props.image}/>
参考文献
今からはじめるReact.js〜propsとstate、それからrefs〜
React における Stateと Props の違い
###stateについて
イベントに対して変化する値のことをstateという。stateは「定義」「表示」「変更」の三段階の処理の記述が必要。
まずはクラス定義の中のコンストラクタでstateにオブジェクト値を代入。stateの定義はクラスの中でthis.state = {};
でできる。
// クラス定義の中身
class ClassName extends ReactComponent{ // ReactComponentクラスを継承したClassNameクラスを定義
constructor(props){ // propsって?
super(props); // 決まり文句(後で調べる)
this.state = {name: '山田'}; // stateを定義
}
}
次にstateを表示する。定義したstateをreturn()
の中でJSXで呼び出す。console.log()
は動作テスト。
render() {
console.log({this.state}); // クラス内で定義したstateの値をコンソールに表示(変化前の値)
return(
<h1>こんにちは {this.state.name} さん</h1> // クラス内で定義したstateの値をブラウザに表示(変化前の値)
);
}
最後にstateの変更について。定義したstateをイベントハンドラで渡すことで値を変更する。例えばonClick
したときにthis.setState(変更後の値)
を渡す。stateの値を変更するときはthis.state
への単なる代入ではなくsetState
メソッドを必ず使う。
下記のコードは<button>
をクリックすると<h1>
の内容が変更されるようになっている。
<button onClick={() => {this.setState({name: 山田});}}>山田</button> // ボタンをクリックしたらstateを変更(変化後の値)
<button onClick={() => {this.setState({name: 田中});}}>田中</button>
stateの「定義」「表示」「変更」の処理のコード全体はこんな感じ。
class ClassName extends ReactComponent{
constructor(props){
super(props);
this.state = {name: '無名'}; // ①stateを定義
}
render() {
return(
<h1>こんにちは {this.state.name} さん</h1> // ②stateを表示
<button onClick={() => {this.setState({name: 山田});}}>山田</button> // ③stateを変更
<button onClick={() => {this.setState({name: 田中});}}>田中</button>
);
}
}
###イベントハンドラでstateを更新する例
onClick
に渡していたsetState
の処理をhandoleClick
というメソッドとして定義する。そしてhandleClick
メソッドをonClick
に渡すことで、同じ処理になる。メソッド定義の部分で複雑な部分をテンプレート化できるので、JSX表記の量を削減できるし、管理しやすい。メソッドはクラスの中で定義するのでthis
でアクセスする。
class ClassName extends ReactComponent{
constructor(props){
super(props);
this.state = {name: '無名'};
}
handleClick(name) { // クリックした時の処理のメソッド定義。複数の名前に対応させるため引数nameを渡す。
this.setState({name: name}); // さっきまでonClickに渡してたのと同じ処理
}
render() {
return(
<h1>こんにちは {this.state.name} さん</h1>
<button onClick={() => {this.handleClick('山田')}}>山田</button> // onClickでhandleClickメソッドが実行される
<button onClick={() => {this.handleClick('田中')}}>田中</button>
);
}
}
###stateが必要なアプリの例
例えばブラウザで秒刻みタイマーを表示する場合、ReactDOM.render()
を1000ミリ秒おきに実行する。このソースコードをコンポーネントでカプセル化して、シンプルかつ正しく動作するよう編集していく。
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2> {/* Dateクラスから取得した時間情報のうち時間の文字列のみを出力 */}
</div>
);
ReactDOM.render( // DOMをレンダリングする処理
element, // 定数elementの中身の要素を
document.getElementById('root') // id="root"の中に
);
}
setInterval(tick, 1000); // 1秒後とにtick関数を実行
まずはReactDOM.render()
でレンダリングする定数elementをコンポーネント化する。
function Clock(props) { // tick関数の中に含まれていた定数elementを、外部で関数elementとして定義する
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2> {/* Dateクラスが入れられる予定の変数dateがpropsにあたる */}
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />, // <Clock/>タグにdate属性を渡してDate()?????
document.getElementById('root')
);
}
setInterval(tick, 1000); // 1秒おきに<Clock/>をレンダリングするtick関数を実行
このままでは秒刻みタイマーの動作のためにDOMを毎秒更新させることになってしまう。合理的な動作のためには<Clock/>
のみを毎秒更新させるべき。
setInterval(tick, 1000); // こいつを<Clock/>の中で処理したい
これを実装するには<Clock/>
コンポーネントにstate
を追加する。stateは〜
まずClock関数をClockクラスに定義しなおす。propsの表記がthis.propsになっていることに注意。
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
参考文献
state とライフサイクル - React公式ドキュメント
意外と知られていないJavaScriptのnew Date()の使用方法
#【実践】WEBサイトにReact.jsを導入する方法
#【勉強】JavaScriptコーディング
###mapメソッド
mapメソッドは配列全てに対して処理を行うもの。forEachに似ているが、mapには戻り値があるところが違う。
render() {
const nameList = ['山田', '田中', '中山'];
return(
<div>
{nameList.map((nameItem) => {
return <p>{nameItem}</p>
})}
</div>
);
}
> 山田
> 田中
> 中山
具体的な使い方の例として、mapメソッドを使ってユーザリストを一覧表示する処理の手順を分解してみる。
- MyApp.jsの
MyApp
クラスのrender(){}
内で、ユーザ情報のオブジェクトをuserList
という配列で管理 - User.jsの
User
クラスのreturn()
で定義した独自タグ<User/>
をエクスポート - MyApp.jsの
MyApp
クラスのreturn()
内で、<User/>
タグに配列の値を代入 - MyApp.jsの
MyApp
クラスのreturn()
で定義した独自タグ<App/>
をエクスポート - index.jsの
ReactDOM.render()
内で<App/>
を書き出すhtml要素のid='root'
を指定 - index.htmlの
<div id="root">
の中に<App/>
が書き出される - そうして
userList
の数だけ列挙した<User/>
を包括した<App/>
をindex.htmlのid="root"
に書き出せる
// MyApp.js
import React from 'react';
import User from './User'; // User.jsで定義したUserクラスを読み込んで<User/>タグを使えるようにする
class MyApp extends React.Component {
render() {
const userList = [ // ユーザ情報のオブジェクトが入った配列
{ name: '山田', image: 'http...png' },
{ name: '田中', image: 'http...png' },
{ name: '山中', image: 'http...png' },
]
}
return(
<div>
<h1>ユーザリスト</h1>
<div>
{userList.map((userItem) => { // userListの配列の全インデックス(ユーザリスト)に対して処理を行う
return (
<User name={userItem.name} image={userItem.image} /> // <User/>タグのプロパティ(nameとimage)にuserListのオブジェクト値を代入
)
})}
</div>
</div>
);
}
export default MyApp; // <MyApp/>タグが使えるようになる呪文
// User.js
import React from 'react';
class User extends React.Component {
render() {
return ( // <User/>タグの中に吐き出すもの一式
<div>
<div>{this.props.name}</div> {// this.propsとは親コンポーネントのMyAppから受け取るための記述方法。}
<img src={this.props.image} />
</div>
);
}
}
export default User; // <User/>タグが使えるようになる呪文
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import MyApp from './components/MyApp';
ReactDOM.render(<MyApp />, document.getElementById('root'));
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="stylesheet.css">
<title>アプリ名</title>
</head>
<body>
<div id="root"></div>
<script src="bundle.js"></script>
</body>
</html>
###コンストラクタの処理をメソッド化
onClick
に渡していたsetState
の処理をhandoleClick
というメソッドとして定義する。そしてhandleClick
メソッドをonClick
に渡すことで、同じ処理になる。メソッド定義の部分で複雑な部分をテンプレート化できるので、JSX表記の量を削減できるし、管理しやすい。メソッドはクラスの中で定義するのでthis
でアクセスする。
class ClassName extends ReactComponent{
constructor(props){
super(props);
this.state = {name: '無名'};
}
handleClick(name) { // クリックした時の処理のメソッド定義。複数の名前に対応させるため引数nameを渡す。
this.setState({name: name}); // さっきまでonClickに渡してたのと同じ処理
}
render() {
return(
<h1>こんにちは {this.state.name} さん</h1>
<button onClick={() => {this.handleClick('山田')}}>山田</button> // onClickでhandleClickメソッドが実行される
<button onClick={() => {this.handleClick('田中')}}>田中</button>
);
}
}
#React.js公式チュートリアルの三目並べゲームを作る
#【実践】WEBアプリをNext.jsで作る方法
#【勉強】公式ドキュメント解読