Help us understand the problem. What is going on with this article?

React初心者のまとめ(基本編:2/3)

More than 1 year has passed since last update.

React初心者がツールを作りながら学習したことをまとめています。
前回の投稿が未読の方はそちらからお読みください。
React初心者のまとめ(基本編:1/3)

JSXはHTMLタグとJSXタグが共存する

分かっている人には当たり前ですが、触ったことない人には意味不明なのがこれ。
JSXではHTMLタグとJSXの自作タグ?(ここでは識別のために「JSXタグ」といいます)が共存します。学習の始めにこの概念がないとわりと混乱します。

<div>
    <MyComponent />
</div>

この場合、<div />が通常タグで、<MyComponent />がJSXタグです。
JSXのルールとして、JSXタグは頭文字を大文字にする必要があります。

HTMLタグとJSXタグと自由に入れ子で配置することもできます。例えば、

<MyComponent>
    <div>テキスト</div>
</MyComponent>

もOKです。

また、JSXタグでは属性も自由に決められます。

<div>
    <MyComponent myTitle={'タイトル'} myValue={this.state.value} />
</div>

属性として記述したものは、子コンポーネントにprops.(定義した属性名)のかたちで渡されます。

class属性を設定する

classはclassNameで書きます。

/* HTML */
<div class="item"> …</div>

↓ ↓

/* JSX */
<div className="item"> …</div>

まとめて属性を渡す

たくさんの属性を渡すとき、下記のように1つずつ渡してもOKですが、数が増えるとなかなか辛いうえに引き継ぐ属性が増えるとその都度引継ぎの記述も増やさないといけなくなります。。

…
constructor(props,context){
   …
   this.state = {title : "aaa", value : "bbb", label : "ccc"}
}
render(){
   <MyComponent
       title={this.state.title}
       value={this.state.value}
       label={this.state.label}
   />
}

解決の方法として、下記の記述で一気に渡すことができます。

render(){
   <MyComponent {...this.state} />
}

イベント

前回でも少し記述したonClickによるクリックイベントです。

eventSample.jsx
class EventSampleComponent extends React.Component{
    clickAction(event){
        alert('clicked!');
    }

    render(){
        return (
            <div onClick={(event) => this.clickAction(event) }>
            クリックしてね
            </div>
       )
    }
}

通常のJavaScript同様に第1引数でイベントを受け取ることができます。
ちなみに

<div onClick={(event) => this.clickAction(event) }>
  ↓
<div onClick={this.clickAction()}>

でも良さそうな気がしますが、この場合ページの表示時にthis.clickAction()の値を評価してしまうため、クリックの有無に関係なくレンダリング時にclickActionが起動してしまいます。

また、

<div onClick={(event) => this.clickAction(event) }>
  ↓
<div onClick={this.clickAction}>

これなら良さそうな気がするのですが、今度はclickActionへthisの引継ぎができなくなるので、期待通り動かないパターンもあります。例えば、

eventSample2.jsx
class EventSampleComponent extends React.Component{
    constructor(props,context){
        super(props,context)
        this.state = { clickText : 'clicked!'}
    }
    clickAction(event){
        alert(this.state.clickText); // -> undefined!
    }

    render(){
        return (
            <div onClick={ this.clickAction }>
            クリックしてね
            </div>
       )
    }
}

こんな書き方の時にundefinedになります。
参考サイト:https://stackoverflow.com/questions/34226076/why-is-my-onclick-being-called-on-render-react-js

なおイベントはonClickだけでなく通常のjavaScript同様に色々とあります。
もっと詳しく知りたい方はコチラへどうぞ。

もっと詳しく: https://reactjs.org/docs/events.html

上位階層から関数を引継ぎ(Props)

上位階層からpropsで値を引き継ぐのと同様に、関数も引継ぐことができます。

class ChildComponent extends React.Component {
  render() {
    return (
      <button onClick={this.props.clickAction}>
        Foo
            </button>
    );
  }
}

class ParentComponent extends React.Component {
  clickAction() {
    alert('clicked!');
  }

  render() {
    // 関数をpropsで引継ぎ
    return (
      <div>
        <ChildComponent clickAction={() => this.clickAction()} />
      </div>
    )
  }
}

コンポーネントを別ファイルに分割する

コンポーネント設計のReactは簡単にファイル分割ができます。

/subComponent.jsx
import React from 'react';

export default class SubComponent extends React.Component {
    render(){
        return(
            <p>sub component</p>
        )
    }
}
/mainComponent.jsx
import React from 'react';
import ReactDom from 'react-dom';

import SubComponent from './subComponent.jsx';

class MainComponent extends React.Component {
    render(){
        <div>
            <p>main Component</p>
            <SubComponent />
        </div>
    }
}

ReactDOM.render(
  <MainComponent  />,
  document.getElementById('main')
);
  • subComponent.jsx

    export default class SubComponent extends React.Component {…}
    

    クラス宣言時に"export"を付けることで、モジュール化することができ、読み込み用の外部ファイルにできます。
    また"default"もあわせて付けると、デフォルトで読み込むモジュールになります。(後述)

  • mainComponent.jsx

    import SubComponent from './subComponent.jsx'; 
    

    で外部ファイル./subComponent.jsxをSubComponentの名前で読み込みます。
    名前は読み込むファイル内のモジュール名と同じにする必要はありません。
    また、読み込むファイル内に複数のモジュールがあったとき、この記述ではdefaultが付いているクラスを読み込みます。
    もしdefault以外のモジュールを読み込みたいときは

    import {component2} from './subComponent.jsx'; 
    

    というかたちで{ }を付けてクラス名を書く形で読み込みます。

    1ファイル内に読み込みたいモジュールが複数ある場合はカンマ区切りで一括読み込みできます。

    import {component2, component3} from './subComponent.jsx'; 
    

シンプルなコンポーネントを作る(Stateless functional components)

Stateを自身で持たないシンプルなコンポーネントの場合は、React.Componentを継承したクラスを作らなくてもコンポーネントを作成できます。

const RenderHello = props => {
    return <div>Hello {prop s.text}</div>;
}

ReactDOM.render(
    <RenderHello text="World" />,
    document.querySelector('content')
);

入れ子のコンポーネントを作る(props.children)

HTMLテンプレートのように、外枠となる部分を共通コンポーネントとして用意したい場合は下記のような記述で書けます。

// テンプレートとなるコンポーネント
class TemplateComponent extends React.Component {
  render() {
    return (
      <main>
        <header>
          <h2>{this.props.title}</h2>
        </header>
        <div>
          {this.props.children}
        </div>
        <footer>
          <p>copyright company</p>
        </footer>
      </main>
    )
  }
}

// コンテンツとなるコンポーネント
class ChildComponent extends React.Component {
  render() {
    return (
      <p>コンテンツテキストです。</p>
    )
  }
}

// 基本レイアウトコンポーネント
class MainComponent extends React.Component {
  render() {
    return (
      <section>
        <h1>Main Title</h1>
        <TemplateComponent title={'コンテンツタイトル'}>
          <ChildComponent />
        </TemplateComponent>
      </section>
    )
  }
}

props.childrenに入れ子の子要素となるコンポーネントが入っているため、それを記述すれば内部に子要素がレンダリングされます。これを使うことで、コンポーネントの内部要素を意識することなくコンポーネントが作れます。

n個のリストをレンダリングする

HTMLで<ul><li>…で表記する時のように、アイテムが複数個ある場合はループで回してレンダリングできます。

listItem1.jsx
// 子アイテム表示用
const RenderItems = items => {
    return items.map((_item,_key) => {
        return(
            <li key={_key}>{_item.value}</li>
        )
    })
}

class ListComponent extends React.Component {
    constructor(props,context){
        super(props,context)
        this.state = { items : [
            { value : 'apple' },
            { value : 'orange' },
            { value : 'banana' },
        ]}
    }
    render(){
        return(
        <div>
            <ul>
            {RenderItems(this.state.items)}
            </ul>
        </div>
        )
    }
}

mapで回して1つずつアイテムを作ったものを親コンポーネントに戻しています。
なお、Reactのルールとして、ループで回して作る子アイテムにはkey属性を振る必要があります。
(<li key={_key}>のところ)

また上記の例ではコンポーネントを分けて書きましたが、下記のような書き方でメソッドとして記述してもOKです。

listItem2.jsx
class ListComponent extends React.Component {
    constructor(props,context){
        super(props,context)
        this.state = { items : [
            { value : 'apple' },
            { value : 'orange' },
            { value : 'banana' },
        ]}
    }
    RenderItems(){
        return this.state.items.map(_item => {
            return(
                <li>{_item.value}</li>
            )
        })
    }
    render(){
        return(
        <div>
            <ul>
            {this.RenderItems()}
            </ul>
        </div>
        )
    }
}

使い回す予定のない子コンポーネントであれば、こちらの方がまとまりのある記述になるので良いかもしれません。

CSSによるデザイン適用

Reactではデザインに関する記述をする機能はありません。
ただしCss in JSと言われる考え方が登場し、デザインもJSの中に含めて考える形が主流となる可能性があります。
例えばjQueryで外部のライブラリを読み込んで利用することはよくありますが、その場合JSとCSSの両方を読み込むことが一般的です。
もしCSSもJSの中に含めることができれば、1行のimport記述と1行のコンポーネント行を記述するだけでデザイン含め機能をすべて読み込むことができるという、非常に高い利便性を実現できます。

(2018/09/21:追記)
こちらに紹介した方法では疑似要素が使えないなどのデメリットもあり、実案件ではこちらの方法を採用していますので参考にして下さい。
https://qiita.com/tomi_shinwatec/items/d3bbc1735bd2ce828d95

CSS適用は下記のような方法で実現できます。

// スタイルを定義
const Styles= {
    bgRed : {
        backgroundColor: "red",
    },
    bgBlue : {
        backgroundColor: "blue",
    }
}

// pにスタイル適用
class MyComponent extends React.Component {
  render() {
      return(
          <div>
              <p style={Styles.bgRed}>背景:赤</p>
              <p style={Styles.bgBlue}>背景:青</p>
          </div>
      )
  }
}

なおHTMLではタグに複数のクラスを追加して複数のスタイルを適用させることはよくありますが、下記のような関数を用意することで、スタイルの組み合わせも容易にできます。
参考:https://qiita.com/koba04/items/0e81a04262e1158dbbe4

const mixStyles = function(){
  var res = {};
  for (var i = 0; i < arguments.length; ++i) {
    if (arguments[i]) Object.assign(res, arguments[i]);
  }
  return res;
}

こんな感じで使います。

// スタイルを定義
const Styles = {
  bgRed: {
    backgroundColor: "red",
  },
  bgBlue: {
    backgroundColor: "blue",
  },
  fontSize16: {
    fontSize: "16px",
  },
  fontSize20: {
    fontSize: "20px",
  }
}

// pにスタイル適用
class MyComponent extends React.Component {
  render() {
    return (
      <div>
        <p style={mixStyles(Styles.bgRed, Styles.fontSize16)}>背景:赤,文字サイズ:16</p>
        <p style={mixStyles(Styles.bgRed, Styles.fontSize20)}>背景:赤,文字サイズ:20</p>
      </div>
    )
  }
}

もっと詳しく: https://reactjs.org/docs/faq-styling.html#what-is-css-in-js


今回は以上になります。挫折せずに学習できていますでしょうか。
次回はこれまでの知識を踏まえた実際のコードサンプルと、ルーティング機能(React Router)について説明します。

続きの記事:React初心者のまとめ(基本編:3/3)

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away