Reactでよく使用されるES2015以降の文法やJSXシンタックス

Reactでよく使用されるES2015以降の文法やJSXシンタックス

先述したとおり、通常Reactでは、ES2015以降やTypeScriptなどのAltJSで記述します。ここではReactでよく使われるES2015以降の文法やJSXのシンタックスについて紹介していきます。全部を網羅することがテーマではないので、よく使われるものに絞って紹介します。ここで紹介するものは極々少数であり、Async AwaitやGenerator, Proxy などの機能は紹介していません。 ES2015の便利な機能をもっと深く知りたい方は書籍が何冊か販売されていますので、手にとって学んでみるのも良いと思います。

速習ECMAScript2018: 次世代の標準JavaScriptを今すぐマスター! 速習シリーズ

CodeSandboxCodePenといったサービスでES2015以降のシンタックスを気軽に試すことができるので、手軽にブラウザで機能を試してみたい方や写経しつつ手元で動かしながら学習をすすめた方におすすめです。ぜひ、他のWebサイトや書籍のコードを見ながら打ち込んでみてください。

CodeSandboxでES2015以降のシンタックスを試す

ES2015以降のシンタックス

アロー関数 (Arrow Functions)

名前のとおりアロー (矢印) 【=>】 を使って定義する関数です。 function という冗長な宣言を省略して、短い形で関数を記述することができます。ES2015以降は特殊な場合を除いて、基本的には functionキーワードを使わず、アロー関数で関数を定義していきます。

また、アロー関数を使うことで this の参照を宣言時のスコープにすることができます。最近ではあまり見なくなった形式ですが、下記のコードでは setInterval()中のthis.counterが Counter を指さないため、counterが1秒ごとに増加していくようには動作しません。setInterval()内部のthisが通常ではグローバルオブジェクトを参照するためです。このようにJavaScriptの「this」には呼び出しコンテキストによって参照が変わるという注意すべき性質がありました。

function Counter() {
  this.count = 0;

  setInterval(function increment() {
    /* thisがCounterを指さないため、this.countはundefinedになる */
    console.log(this.count)
    this.count++;
  }, 1000);
}

new Counter();

setIntervalで用いる関数を、アロー関数で定義すると、thisが関数宣言時のスコープを参照するようになるため、setInterval()内部での「this」は外の関数の「this」になります。そうして、Counter()で定義されているthis.countが正常に参照できるようになります。

function Counter() {
  this.count = 0;

  setInterval(() => {
    // アロー関数を用いることでthisが宣言時のスコープを参照するようになり、外側のcountが1秒ごとに増えていく。
    console.log(this.count);
    this.count++;
  }, 1000);
}

new Counter();

また、アロー関数ではステートメントが一文のみの場合には returnを省略して、より簡潔に、短く書くことができます。

// 下記のfuncAはfuncBと同義です。
const funcA = () => "hello";

const funcB = () => {
  return "hello";
};

// 下記のfuncCはfuncDと同義です。
const funcC = () => ({
  curry: "India",
  tacos: "Mexico"
});

const funcD = () => {
  return {
    curry: "India",
    tacos: "Mexico"
  };
};

クラス (Class)

ES2015から、他の言語と同じように クラス構文 (Class )が使えるようになりました。Reactではコンポーネントをクラスで定義することができますが、積極的にクラスを使いましょうということではなく、 基本的には関数でコンポーネントを定義 していき、必要な時にクラスを使います。その方が簡潔に書けるというメリットもあります。

import React from "react";
import ReactDOM from "react-dom";

/* 関数でコンポーネントを定義する */
const FunctionalComponent = () => <h2>FunctionalComponent</h2>;

/* クラスでコンポーネントを定義する */
class ClassComponent extends React.Component {
  render() {
    return <h2>ClassComponent</h2>;
  }
}

const App = () => (
  <div>
    <FunctionalComponent />
    <ClassComponent />
  </div>
);

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

テンプレート文字列 (Template Literal)

「`」バッククォートで文字列を囲むことにより、文字列の中に式や変数を埋め込むことができるようになっています。変数や式を展開するときには「${}」で囲みます。

const hello = "Hello";
const printWorld = () => "World";

// "Hello, World"
console.log(`${hello}, ${printWorld()}`);

const isCurryHot = () => true;

// "HOT!"
console.log(`${isCurryHot ? "HOT!" : "Not Hot..."}`);

分割代入 (Destructuring Assignment)

分割代入を使うことで配列の要素やオブジェクトからプロパティを取り出して、それぞれ別の変数に代入することができます。

const getObject = () => ({
  JR: "Chuo",
  Metro: "Tozai",
  Toei: "Ooedo"
});

const myFunc = () => {
  const { JR, Toei } = getObject();

  // Chuo
  console.log(JR);

  // Ooedo
  console.log(Toei);
};


const metro = [
  "Tozai",
  "Ginza",
  "Nanboku",
  "Yurakucho",
  "Marunouchi",
  "Chiyoda"
];

const [line1, line2] = metro;

// Tozai Ginza
console.log(line1, line2)

スプレッド構文 (Spread Operator)

スプレッド構文 (Spread Operator)は「...」というドット3つのドットをオブジェクトや配列の前に記述することで、それらの要素を展開することができるシンタックスです。言葉で説明しても解りにくいので、例を見てみましょう。

//1: オブジェクトをスプレッド構文 (ドット 3つ) で展開
const cities = {
  Sinjuku: "Tokyo",
  Mito: "Ibaraki",
  Urawa: "Saitama"
};
console.log({
  ...cities,
  Yokohama: "Kanagawa",
  Takasaki: "Gunma",
  Funabashi: "Chiba",
  Utsunomiya: "Tochigi"
});
/*
{
  Sinjuku: "Tokyo"
  Mito: "Ibaraki"
  Urawa: "Saitama"
  Yokohama: "Kanagawa"
  Takasaki: "Gunma"
  Funabashi: "Chiba"
  Utsunomiya: "Tochigi"
}
*/

//2: 配列をスプレッド構文 (ドット 3つ) で展開
const foods = ["Natto", "Soba", "Udon"];
console.log(...foods, "Curry", "Sushi", "Tofu");
// ["Natto", "Soba", "Udon", "Curry", "Sushi", "Tofu"]

JSX

Reactではコンポーネントを定義するために JSX というJavaScriptを拡張した文法を使用します。JSX を用いることで、マークアップ (HTML)とスタイル (CSS)、ロジック (JavaScript)を分離せずに、ワンセットのコンポーネント としてすべてJavaScriptの内部で扱えるようになります。

CodeSandboxでJSXを試す

const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;

ReactDOM.render(
  element,
  document.getElementById('root')
);

Spread Attributes

親コンポーネントから子コンポーネントにPropsが渡されてきていて、それをそのまま孫コンポーネントに渡したい場合には、逐一Propsを属性として定義していくのは億劫です。そのままPropsを子コンポーネントに渡したい場合には、スプレッド構文のように 「{...props}」として、そのままPropsを子コンポーネントに渡すことができます。

import * as React from "react";
import ReactDOM from "react-dom";

const EmojiList = props =>
  props.emojis.map(emoji => <div key={emoji}>{emoji}</div>);

const Emoji = props => (
  <div>
    <h2>{props.title}</h2>
    <EmojiList emojis={props.faces} />
    <EmojiList emojis={props.fruits} />
    <EmojiList emojis={props.animals} />
  </div>
);

const App = props => (
  <div>
    <Emoji {...props} />
  </div>
);

/* 下記のように逐一 Propsを子コンポーネントに渡すこともできますが、億劫です。 */
/* Spread Attributesを使うことで 「{...props}」という記述だけでPropsをそのまま、まとめて子コンポーネントに渡せます。 */
// const App = props => (
//   <div>
//     <ul>
//       <Emoji
//         title={"Emoji"}
//         fruits={props.fruits}
//         faces={props.faces}
//         animals={props.animals}
//       />
//     </ul>
//   </div>
// );

const faces = ["😀", "😅", "🤔", "👻", "👺"];
const fruits = ["🍇", "🍈", "🍊"];
const animals = ["🐳", "🐉", "🦖"];

ReactDOM.render(
  <App faces={faces} fruits={fruits} animals={animals} />,
  document.getElementById("root")
);

Spread Attributes (本家ドキュメント)

Conditional Rendering

Inline If with Logical && Operator

何らかの条件をもとにして、コンポーネントのレンダリングを制御したい場合には && を使うと便利です。JavaScriptでは 「true && A」の場合はAを返し、「false && B」の場合にはfalseが返ってくるので、単純なtrueかfalseの結果を元にしてコンポーネントを出し分けたい場合には一行で書くことができます。

下記の例では、2で割った余りが0である場合にはtrueが返る isEven() 関数を定義しており、2を渡しているので、下記のコンポーネントがレンダリングされます。もし、3や5などの数字をisEven()に渡すと下記コンポーネントはレンダリングされません。

<span>Even!</span>
import React from "react";
import ReactDOM from "react-dom";

const isEven = num => num % 2 === 0;

const App = () => <div>{isEven(2) && <span>Even!</span>}</div>;

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Inline If-Else with Conditional Operator

何らかの条件を基にして、trueかfalseの場合で、別々のコンポーネントを出し分けたい場合には下記のように三項演算子を使うと便利です。

下記ではisLoggedInの値をもとにして、trueが返ってくる場合には「Logout」ボタンを、falseが返ってくる場合には「Login」ボタンを出力します。

import React from "react";
import ReactDOM from "react-dom";

const isLoggedin = () => true;

const App = () => (
  <div>{isLoggedin() ? <button>Logout</button> : <button>Login</button>}</div>
);

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Conditional Rendering (本家ドキュメント)