かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

React JSX の中で if で分岐させたい

React のアプリで例えば編集モードの時にはフィールドで、そうでない時はテキストを表示するなど、条件によって表示を変えたい時など JSX 内で分岐をさせたい場合とか。 ふと思った。

JSX 内で直接 if 文は使えない

JSX は { } 内で JavaScript が実行できるものだと思いこんでいたのですが、実行できるのは式 ( expression ) だけで、iffor のような文は実行できないようです。

if statements and for loops are not expressions in JavaScript, so they can’t be used in JSX directly.
ref. JSX In Depth – React

直接 if 文を使おうとするとエラーになります

function Content(props) {
  return (
    <div className="content">
      {
        if (props.isEdit) {
          return (
            <form onSubmit={props.onUpdate}>
              <input type="text" value={props.text} />
              <button type="submit">UPDATE</button>
            </form>
          )
        } else {
          return (
            <p className="text">{props.text}</p>
          )
        }
       }
    </div>
  );
}

=> Error:Parsing error: Unexpected token`

即時関数内で if 文を使う

関数は JSX 内で実行できるので、即時関数を使ってその中で if 文で分岐させ出力するコンポーネントreturn する

function Content(props) {
  return (
    <div className="content">
      {
        !function() {
          if (props.isEdit) {
            return(
              <form onSubmit={props.onUpdate}>
                <input type="text" value={props.text} />
                <button type="submit">UPDATE</button>
              </form>
            );
          } else {
            return (
              <p className="text">{props.text}</p>
            );
          }
        }()
       }
    </div>
  );
}
Arrow 関数でもOK
function Content(props) {
  return (
    <div className="content">
      {
        (() => {
          if (props.isEdit) {
            return( ... );
          } else {
            return ( ... );
          }
        })()
       }
    </div>
  );
}

即時関数の注意点 最後に ; があるとエラーになる

JSX 内で即時関数を使う場合、関数を実行する (); が付いているとエラーになりるようです。

function Content(props) {
  return (
    <div className="content">
      {
        (() => {
          // ...
        })();
        /* ↑ ; があるとエラーになる */
       }
    </div>
  );
}

=> Parsing error: Unexpected token, expected "}"

三項演算子を使う

三項演算子演算子なので JSX 内で使うことができる

function Content(props) {
  return (
    <div className="content">
      {
        props.isEdit?
          (<form onSubmit={props.onUpdate}>
            <input type="text" value={props.text} />
            <button type="submit">UPDATE</button>
          </form>)
          :
          (<p className="text">{props.text}</p>)
      }
    </div>
  );
}

三項演算子で直接 HTML を返すような書き方は、個人的に見通しが悪いように思うのであまり良くなさそうかなと思いました。出力する HTML を別コンポーネントにすればまだ見通しが立つように思います。

function InputField(props) {
  return (
    <form onSubmit={props.onUpdate}>
      <input type="text" value={props.text} />
      <button type="submit">UPDATE</button>
    </form>
  );
}

function ContentBody(props) {
  return (
    <p className="text">{props.text}</p>
  );
}

function TestComponent(props) {
  return (
    <div className="content">
      {
        props.isEdit?
          (<InputField {...props} />)
          :
          (<ContentBody {...props} />)
      }
    </div>
  );
}

三項演算子の場合も即時関数のときと同じで最後に ; があると Parsing error になる。

true の時だけ表示したいような場合は && が利用できる

JavaScript の比較演算子 &&, || は Bool値を返すのではなく値を返すことを利用すれば、{(条件) && <trueの時に返される値>} と書くことができます。

function TestComponent(props) {
  return (
    <div className="content">
      {props.isNew && (<span>NEW</span>)}
      // ...
    </div>
  );
}
note.
  • || は 左辺が true なら左辺を返し、左辺が false なら右辺を返す
  • && は 左辺が true なら右辺を返し、左辺が false なら左辺を返す

コンポーネントの形で関数を呼び出し、関数内で if 文などを使う

JSX の <Component /> という記法で Function Component を呼び出せているので、呼び出した関数内では通常の JavaScript が使用できるから、そこで分岐など行えば良い

function InputField(props) {
  return ( ... );
}

function ContentBody(props) {
  return ( ... );
}

function TestComponent(props) {
  const NewLabel = (isNew) => {
    if ( isNew ) {
      return (<span>NEW</span>);
    }
  }

  const Content = (props) => {
    if ( props.isEdit ) {
      return (<InputField {...props} />)
    } else {
      return (<ContentBody {...props} />)
    }
  }

  return (
    <div className="content">
      <NewLabel isNew={props.isNew} />
      <Content {...props}/>
    </div>
  );
}

この方法が個人的には一番見通しが良くメンテナンスもしやすそうだなと思いました。

ポエム

JSX の { } 内で直接実行できるのは式 ( expression ) だけというのは、勘違いしてたので知れてよかったです。( どれが式なのかとかちゃんと理解できて入わけではないのですが… Vue.js には v-ifv-elsev-for という制御するための構文が用意されていますが、もしかする JSX が構文使えないことを改善するために用意されたのかもなーと思いました。(属性で制御するのが見やすいかどうかは、あまり使ってないから何とも言えないし慣れの問題な気もする)

と、即時関数や三項演算子 && などを使えば JSX 内で分岐させる事もできるのですが、個人的に見通しが悪くなるように感じました。
React のチームが JSX 内で if などの文を直接使えなくしているのは、構文を使わなければならないようなモノは別のコンポーネントにしろっていう思想なのではないかなー。と思ったのでした。

別件だけど Arrow関数の即時関数 !() => {}()と書けないみたいなので、改めて調べたい。


[参考]

入門 React ―コンポーネントベースのWebフロントエンド開発

入門 React ―コンポーネントベースのWebフロントエンド開発