JavaScript
reactjs
React

React.jsの知っておいて損はないTips

More than 1 year has passed since last update.

React.jsでの開発を始めてから微妙にハマったポイント、また「これを早めに知っておけばもっとコードを簡潔に書けたのに・・!」という点がいくつかあったので、それらのTipsを共有します。

renderで返すComponentは単一の親でなければならない

まずは入門編。Reactを始めた人は一度はエラーになったことがあるかも。
renderメソッドで返すのは単一の親のComponentであり、複数の親のComponentを返すことは出来ません。
必ず一つの親になるようにWrapしてあげましょう。

// エラーになる
render(){
  return(
    <div>aaa</div>
    <div>bbb</div>
  );
}

// 正常
render(){
  return(
    <div>
      <div>aaa</div>
      <div>bbb</div>
    </div>
  );
}

renderで空を返す

子コンポーネントで親から渡されたデータによって空のComponentを返したい場合があります。
そんな時は空のHTML要素を使わなくてもnullかfalseで返せます。
(ただしundefinedは使えません)

render() {
  if (!this.props.isShow) {
    return null; // もしくはreturn false;
  }
  return (
      <div>
        表示します
      </div>
  );
}

JSX内でif/else文の判定やループを行う

条件分岐による要素の変更やループでの要素作成を行う場合、まず変数に代入しておいて、最後のreturn時にJSX内で変数を割り当てる方法があります。

render() {
  const label = this.props.isShow ? <div>テスト</div> : null;
  const commentNodes = this.props.data.map((comment) => {
    return (
      <Comment author={comment.author}>
        {comment.text}
      </Comment>
    );
  });
  return (
    <div>
      {label}
      <div className="commentList">
        {commentNodes}
      </div>
    </div>
  );
}

または規模がある程度大きい場合子コンポーネントに分割するのを検討します。
しかしごく単純な処理はJSX内でインラインで直接条件分岐やループを書きたい場合もあります。

そこでJSXのインラインで処理する記法が以下になります。

render() {
  return (
    <div className="commentList">
      {(() => {
        if (this.props.isShow) {
          return <div>テスト</div>;
        }
      })()}
      {(() => {
        return (
          this.props.data.map((comment)=> {
            return <Comment author={comment.author}>
              {comment.text}
            </Comment>
          })
        );
      })()}
    </div>
  );
}

アロー関数を用いた即時関数で処理する方法ですね。
これはReact公式のドキュメントにも記載してあります。
https://facebook.github.io/react/tips/if-else-in-JSX.html

ただこの記事に対するコメントでも指摘があったのですが、即時関数を使う必要は無く、もっと簡潔に書けます。

というわけで↑のコードをLet's修正。

render() {
  return (
    <div className="commentList">
      {this.props.isShow && <div>テスト</div>}
      {this.props.data.map((comment)=> {
          return (
            <Comment author={comment.author}>
              {comment.text}
            </Comment>
          );
      })}
    </div>
  );
}

うん、見通しが良くなった笑

{this.props.isShow && <div>テスト</div>}

の部分は三項演算子でも構いません。

{this.props.isShow ? <div>テスト</div> : null}

ただ全てにおいてインラインで書けば良いというわけではなく、ケースによってコンポーネントの分割やrenderの切り出しを使い分けましょう。
(指摘して頂いた皆さんありがとうございます :pray: )

onKeyDown/onKeyPress/onKeyUpイベントでのキーコード取得の違い

実はこの3つのイベントの中でonKeyPressだけキーコード取得の方法が違います。
onKeyDown/onKeyUpは引数のイベントのkeyCodeプロパティから取得できますが、onKeyPressでkeyCodeプロパティから取得しようとしても常に0が返ってきます。
onKeyPressでの正しいキーコードの取得はkeyCodeプロパティではなくcharCodeになります。

onKeyPress(e) {
  console.log(`onKeyPress KeyCode:${e.charCode}`)
}

onKeyDown(e) {
  console.log(`onKeyDown KeyCode:${e.keyCode}`)
}

onKeyUp(e) {
  console.log(`onKeyUp KeyCode:${e.keyCode}`)
}


render() {
  return (
      <div>
        <form onSubmit={this.onSubmit}>
          <input type="text" onKeyDown={this.onKeyDown} onKeyPress={this.onKeyPress} onKeyUp={this.onKeyUp}/>
          <button type="submit">登録</button>
        </form>
      </div>
  );
}

classNameに文字列と変数を組み合わせて割り当てる

状態によってclass名を割り当てデザインを変更したい場合は往々にあります。

例えば固定のclass名であればダブルクオーテーションでくくります。

<div className="header">

次に変数を割り当てたい場合は{}でくくります。

<div className={showClass}>

それでは固定のclass名と変数の組み合わせたい時はどうでしょうか?
間違いがちなのが下の書き方ですね。

<div className="header {showClass}">

これはエラーになります :no_good:

記法として二つあります。
まずこれも一つの変数に代入してから割り当てる方法

const headerClass = "header " + (isShow ? "show" : "hide"); 

...

<div className={headerClass}>

二つ目はES6のテンプレートリテラルを使用してインラインで書きます。

<div className={`header ${isShow ? "show" : "hide"}`}>

僕は二つ目の方法をよく使います。
(個人的にテンプレートリテラルはES6で最も便利な文法の一つだと思ってます :star: )

他に思いついたTipsがあったら追記していきますので宜しくお願いします。