ほぼ日刊サービス開発日誌

React, firebase, 機械学習など

sponsored

Webデザイナーさんに伝えるために書いた最速でコーディングに入るためのReact.js初心者向け概論

一緒にやっているWebデザイナーさんに初めてReactアプリのコーディングを頼むときに書いた概論です。

最速でコーディングに入ることを目的としていた文章ですので、

ザックリしすぎる一般化もありますし、深いReact理解/実装を求めるなら公式リファレンスを最初から最後までやるのが一番です。

記事を読んでから行うコーディングとしては

  • htmlの組み直し
  • スタイリングに利用するためのクラス/idの付与
  • 動的なスタイリング

を念頭に書いています。

Reactとは?

javascriptでhtmlを書くという発想の、フロントエンドを司るプログラミング言語。 Webの画面、すべてをコンポーネント(Railsでいう、テンプレートのようなもの)で表現する。

一言で言えば、html、ページといった概念からの解放。

たとえば、 <UserIcon> というコンポーネントを定義したとする。いったん定義してしまえば、ユーザーページでも、記事一覧の著者アイコンでも、コメント一覧のコメント主の画像にも使うことができる。いろんなファイルから、 import UserIcon from './to/userIcon/file/path'とするだけで、利用することができるようになる。

何が便利?

stateが便利。Reactの世界では、コンポーネントはそれぞれ、stateつまり状態を持つ。例えば、<NotificationDropdown>という、通知ボッックス全体を表すコンポーネントがあるとしよう。

f:id:serendipity4u:20180925113845p:plain

この NotificationDropdownにはどんなstateが考えられるだろうか?

ドロップダウン式なので、開いているか・開いていないか の2通りのstateがあると便利だろう。最初は閉じているはずなので、isOpen: falseと設定できるといいだろう。

stateは以下のように定義することができる。

import React from 'react'

class NotificationDropdown extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      isOpen: false, // ここに定義する。falseを初期値として入れた
    };
  }
...省略

上記のコードで、Reactを用いて、NotificationDropdownというコンポーネントのクラスを作成し、そのコンポーネントが内部に isOpen=true / false という状態を持つことを定義できた。

状態があると何がいいのか??

NotificationDropdownクラスの中で、

if (this.state.isOpen === true) {
  console.log('開いてるよ!');
  // 新しい通知を読み込んだり...
 render ( <NotificationContent /> )
} else {
  console.log('閉じているよ!'); 
}

簡単に状態を参照できるため、 * 条件分岐に this.state.isOpen の値を用いたり、 * 上記によってisOpen === trueのときだけ別のコンポーネントをレンダリング(表示すること)したりといったことができる(例では<NotificationContent/>

stateの例

  • ユーザーページにおいて、今閲覧しているユーザーがそのユーザーをフォローしているかどうか: this.state.following === true なら<UnfollowButton>コンポーネントをrenderする、など。

app/javascript/packs/compornent/relationButton.jsxは、

f:id:serendipity4u:20180925113913p:plainf:id:serendipity4u:20180925113916p:plain

上記のようなフォロー・アンフォローボタンを表示するクラスである。

このボタンの種類は、

  • <UnFollowButton user={user}>
  • <FollowButton user={user}>

の2種類があって、

this.state.following_status

というstateの値によって、表示するコンポーネントを出し分けているのである。 つまり、render() { }の中身は以下のようになっている。


 if (this.state.following_status === 'following'){
      return(
        <div className="follow_form" onClick={ () => this.unfollow(user)}>
          <UnFollowButton user={user}></UnFollowButton>
        </div>
      )
    } else {
      return(
        <div className="follow_form" onClick={ () => this.follow(user) }>
          <FollowButton user={user}></FollowButton>
        </div>
      )
    }

大事なrender関数

すべてのReactのクラスは、render()メソッドを有する。これは、このクラスが結局何を表示するのか?という問いの、「何」に当たる。


import CommentBox from './components/commentBox.jsx' 

React.render( // 表示する
  <CommentBox />, // このコンポーネントを
  document.getElementById('container') // ここに
);

これはCommentBoxクラスで定義したHTML要素を、#containerの中にレンダリングする(表示する)というコードである。

もっと実践的な例として、

記事のタイトルと画像を表示するArticleContentコンポーネントのクラス定義を見てみよう。 f:id:serendipity4u:20180925114311p:plain


import React, { Component } from 'react';
import { render } from 'react-dom';
import UserIcon from './components/userIcon.jsx'


class ArticleContent extends React.Component {
  constructor(props) {
    super(props);
  }

  render(){
    const item = this.props.item;

    return (
      <section className="article-content">
        <UserIcon user= {item.user} /> // 注目
          <div className="article-content__title">
            <h4 className="title">
              { item.title } // タイトルを表示
            </h4>
          </div>
        <div className="article-content__img">
        </div>
      </section>
    )
  }

}

export default ArticleContent;  //exportすることで、他のページからimportして<ArticleContent>で呼び出せるようになる。

このように、render() { return( htmlの内容 ) }で記述していく。

また、render(){}のなかに、UserIconがあることにも注目してほしい。3行目の

import UserIcon from './components/userIcon.jsx'

で、他のファイルからUserIconというクラスを持ってきていることがわかる。jsxというファイルの拡張子は、javascriptとhtmlを一緒に書くReactのファイルにつける拡張子である。これは'./components/userIcon'と拡張子を省略して読み込むこともできる。

どう別のファイルにあるコンポーネントに変数を渡すのか?

さて、ここで、上記のコードを見て、

 <UserIcon user= {item.user} />

の記述を疑問に思っただろうか。

Reactで、stateの次に重要な概念として、propsがある。propは小道具という意味の英語である。

これは、別のファイルのコンポーネントを表示するときに、そのコンポーネントに、変数を渡してあげる役割を持つ。

itemという変数に、以下のようなjsonデータが格納されていたとしよう。

  "id":141633,
  "url":"https://www.flaticon.com/free-icon",
  "title":"Settings - Free Tools and utensils icons",
  "eyecatch_url":"https://imageog.on.com/7.png",
  "user":
      {
        "username":"kansiho",
        "profile_image":"profile_image1231243.png"
      },

このアイテムの所有者の情報は、item.userで取得できる。<UserIcon user= {item.user} /> としてユーザー情報をUserIconコンポーネントに渡してあげよう。render関数の中のreturnの値は基本的にhtmlタグ要素と解釈されるが、{}で括ることでhtmlタグ要素ではなく、変数として渡すことが可能だ。

(つまり、id="myId"だと"myId"という文字列のへんてこなIDになるけど、id={myId}だとmyIdという変数を代入するという意味になるということ。たとえば var myId="main"id={myId}とすればid="main"になる。)


    return (
      <section className="article-content">
        <UserIcon user={item.user} /> // 注目
          <div className="article-content__title">
            <h4 className="title">
              { item.title } // タイトルを表示
            </h4>
          </div>
        <div className="article-content__img">
        </div>
      </section>

さて、渡された側のUserIconコンポーネントではどう表示したらいいのだろう? UserIconのコンポーネントの中のrender関数の中身を見てみよう。

// app/javascript/packs/component/userIcon.jsx

const user = props.user;
  
return(
    <a href={'/@' + user.username }>
      <div className="user-icon">
        <div>
         <img src={props.user.profile_image} />
         { props.user.username } さん
        </div>
      </div>
    </a>

user={user} で渡した値は、props.userから引き出すことができる。

もし、別の変数名、たとえば以下のようにaccountというprop名 <UserIcon account= {item.user} /> で渡したなら、props.accountから引き出すことができる。つまり、usernameを表示するなら{ props.account.username }となる。

styleはどう適用すべきか?

// app/javascript/packs/component/userIcon.jsx

const user = props.user;
  const iconStyle = user.profile_image ? ( {
    backgroundImage: 'url(' + user.profile_image + ')'
  }) : (null)
  return(
    <a href={'/@' + user.username }>
      <div className="user-icon">
        <div style={iconStyle} className={"user-icon--circle " + (user.profile_image ? '' : ' user-icon--circle--noimg')}>
          { user.profile_image === null && user.username ? user.username.charAt(0) : ''}
        </div>
      </div>
    </a>

ややややこしいのが、動的なスタイルの適用である。 動的というのは、つまり、与えられたjsonデータの内容に応じて背景画像を変えたい、といった場合である。

その場合は、

const iconStyle = {
    backgroundImage: 'url(' + user.profile_image + ')'
}

以上のように、スタイルをオブジェクトで定義し、その中で変数を利用しなくてはいけない。

便利なこととしては、ここにおいて、stateを利用したりできることだろう。

たとえば、三項演算子を用いて、

user.profile_image ? (profile_imageがあるとき) : (profile_imageが無い、つまりnullのとき)

const iconStyle = user.profile_image ? ( 
// あるとき
{backgroundImage: 'url(' + user.profile_image + ')'}
) : (
// ないとき
{backgroundImage: 'url(example.com/default.png)'}
)

のようにして、プロフィール画像がある時と、無い時で、画像を出し分けるといったことができる。

なぜReactを使うのか?

  • より高速に、
  • リッチな体験を、
  • 高い可読性のコードによって届けるため。

json って何?

JSONとはJavaScript Object Notationの略で、XMLなどと同様のテキストベースのデータフォーマット。その名の通り、JavaScriptと相性が良い書き方ルール。

画像引用元: wa3.i-3-i.info

{
 "hogehoge": [ 1, 2 ],
 "piyopiyo": {
  "piyota": [ "21", "lovely" ],
  "id": "200"
 }
}

ブラウザでjsonを確認する際に、見やすいインデント表示するためには、下記URLから拡張機能を入れることをすすめる。

chrome.google.com

ES6

2015年に発表された、よりオブジェクト指向に、かつ、堅牢になったモダンなjavascriptの文法。

ES2015 (ES6)についてのまとめ

を読むといい。

Reactリファレンスを読むにあたって最低限分かっていた方が良い文法まとめ↓

let,constによる変数宣言

constは定数(中身が変わらない変数)を宣言したい時。 letは再代入したい時。 どちらもブロック{}の外への影響力を持たないことに注意する。

let foo = [1, 2, 3];
{
  let foo = [4, 5, 6];
  console.log(foo);
  // => 4, 5, 6
}
console.log(foo);
// => 1, 2, 3

foo = [2,3,4]; // 再代入してもエラーは出ない

const name = 'shiho'
name = 'hanako' // 再代入なので、エラー「Uncaught TypeError: Assignment to constant variable」になる

constructor

constructorメソッドは、クラスがnewされた時に実行されるメソッドであり、クラス内で共通して使われるプロパティの初期値の定義などを行う。1つのクラスに1つしか定義できない。 (Rubyでいうinitializeメソッド)。

class Human {

  constructor(name) {
    this.name = '山田太郎';
  }

  hello() {
    console.log('ぼくのなまえは' + this.name);
  }

}

obj = new Human('アレクサンダー')
obj.hello // ぼくのなまえはアレクサンダー

アロー関数「=>」

従来のfuncitonを使った関数宣言に加えて、=>を用いた関数宣言が可能。

let plus = (x, y) => {
  return x + y;
};
plus(1,6) // 7

配列展開の「...」

var array = [1, 2, 3]
[...array, 4, 5, 6] // [1,2,3,4,5,6] 

どのファイル(コンポーネント)を見れば・いじればいいのか?

chrome.google.com

このchrome拡張機能を入れましょう!

f:id:serendipity4u:20180926120657p:plain

コンポーネント名がデバッガーに表示されます。

次に読むといいかな?

mae.chab.in

qiita.com