Reactの基礎(JSX, ライフサイクル, state, props)

Reactを利用する上で押さえておくと良い基本要素について取り上げます。Vue.jsなどの経験者がReactの公式ドキュメントを一通り読んだあと、Reactの基礎内容を振り返るための内容です。

Hello World

まずは、少ない実装でReactの動作確認をします。id属性がrootの要素内に Hello World と表示されるように実装します。

public/index.html

<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <div id="root"></div>
  </body>
</html>

src/App.js

import React from 'react'

function App() {
  return (
    <div>
      <p>
        Hello World
      </p>
    </div>
  )
}

export default App

src/index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

ReactDOM.render(<App/>, document.getElementById('root'))

ReactDOM.renderでDOMにReact要素を反映してます。

701-react-basic_helloworld.png

実行結果です。

JSX

BabelでJavaScript構文にコンパイル

JSXはJavaScriptの拡張言語です。以下のようにBabelでJavaScript構文にコンパイルできます。

npm install --save-dev babel-cli babel-preset-react
$ cat app.jsx 
<div><p>Hello World</p></div>
$ 
$ 
$ ./node_modules/.bin/babel --presets=react app.jsx 
React.createElement(
  "div",
  null,
  React.createElement(
    "p",
    null,
    "Hello World"
  )
);

JSXはHTMLのように記述でき、直接JavaScriptで記述するより理解しやすいため利用されます。

{}で式の埋め込み

{} 内にJavaScriptの式を記述できます。

const App = () => {
  const sum = (a, b) => a + b

  return (
    <div>
      <p>{sum(3, 4)}</p>
    </div>
  )
}

XSS対策として、デフォルトでHTMLタグをエスケープしてくれます。

className, on[Event]

JavaScriptの予約語とかぶる属性はそのまま利用できません。
例えば、classclassName と指定します。

React.Fragment

returnする際、1つのタグにまとめる必要がありますが、divでまとめると余計なdivタグが追加されてしまいます。

const App = () => {
  return (
    <div>
      <p>aaa</p>
      <p>bbb</p>
    </div>
  )
}

React.Fragmentを利用すると余計なタグが追加されません。

<React.Fragment></React.Fragment> または、 <></> で囲みます。

const App = () => {
  return (
    <>
      <p>aaa</p>
      <p>bbb</p>
    </>
  )
}

参考

コンポーネント

クラス or 関数

関数コンポーネント

import React from 'react'

const App = props => {
  return (
    <div>
      <p>
        {props.message}
      </p>
    </div>
  )
}

export default App

クラスコンポーネント

import React from 'react'

class App extends React.Component {
  render() {
    return (
      <div>
        <p>
          {this.props.message}
        </p>
      </div>
    )
  }
}

export default App

関数コンポーネントでは、クラスコンポーネントが持つ ライフサイクルメソッド state などの機能を実装できませんでした。React16.8で追加された Hooks を利用すれば、関数コンポーネントでもクラスコンポーネントと同様に実装することができます。

コンポーネントの設計の考え方としては、状態を持たない純粋関数で作られたコンポーネントをなるべく多く作り、それらをimportして利用する方式が良いです。テストしやすく、再利用性が高まります。

ライフサイクル

コンポーネントのライフサイクルの特定タイミングで処理を差し込むことができます。

import React from 'react'

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

  componentDidMount() {
    // コンポーネント生成時
  }

  componentDidUpdate() {
    // コンポーネント更新時
  }

  componentWillUnmount() {
    // コンポーネント削除時
  }

  render() {
    return (
      <div>
        <p>Hello world</p>
      </div>
    )
  }
}

export default App

参考

state|コンポーネントの状態

import React from 'react'

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      items: Array(9).fill(0),
    }
  }

  handleClickItem(index) {
    const items = this.state.items.slice()
    items[index]++
    this.setState({ items: items })
  }

  handleResetItems() {
    this.setState({ items: Array(9).fill(0) })
  }

  render() {
    const items = this.state.items.map((item, index) => {
      return (
        <li
          key={index}
          onClick={() => this.handleClickItem(index)}
        >
          {`Button${index}: ${item}`}
        </li>
      )
    })

    return (
      <div>
        <ul>{items}</ul>
        <div onClick={this.handleResetItems.bind(this)}>Reset items</div>
      </div>
    )
  }
}

export default App
701-react-basic_state.png

実行イメージです。

状態を変更させるにはsetStateを利用します。renderメソッド が再度呼び出されます。

関数が this.props this.state などのコンポーネント属性にアクセスできるようにするには、以下のような対応が必要です。

  • onClick={() => this.handleClickItem(index)} のようにアロー関数を利用する
    • アロー関数を利用すると1つ上のthisと同じスコープになってくれる
  • onClick={this.handleResetItems.bind(this)} のようにbind関数を利用する

https://reactjs.org/docs/faq-functions.htmlにて解説されています。

参考

props|読み取り専用

import React from 'react'

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      message: 'Hello',
      flg: true
    }
  }

  handleClick = () => {
    this.setState({ flg: !this.state.flg })
  }

  render() {
    return (
      <div>
        {this.state.flg && <p>aaaaa</p>}
        <Child
          message={this.state.message}
          handleClick={this.handleClick}
        />
      </div>
    )
  }
}

const Child = (props) => (
  <div>
    <p>{props.message}</p>
    <div onClick={props.handleClick}>Click</div>
  </div>
)

export default App

親コンポーネントから props を経由してデータを渡せます。子コンポーネントで発生したイベントに応じて、親コンポーネントの状態を変更したい場合、状態を変更するコールバックを props 経由で渡します。

参考

Developer Tools