Reactをかじる

2020年11月30日

Reactを勉強していたのだが最近ようやくすこしわかるようになってきた。

ReactはJavaScriptライブラリだ。

Hello World

CodePen

開発環境を整えなくてもCodePenで試せる。JavaScriptの設定で、Babalを選び、react及びreact.domを追加する。

  <div id="root"></div>
ReactDOM.render(<h1>Hello, world!</h1>, document.getElementById('root'));

JSXという記法で<h1>等htmlのような書き方ができる。BabelがJavaScriptに変換してくれる。html要素をJavaScriptでたくさん生成すると読みにくいが、JSXではかなりシンプルになる。

CDN(Content Delivery Network)

開発環境を整えなくてもCDNであれば簡単にブラウザで試せる。ただし、表示が遅くなるので正式運用する場合はCDNでなく、開発環境を整えBabelで変換したものをサーバーに置いた方がいい。

scriptでbabel.min.jsを指定して、scriptのtype="text/babel"を書く。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>JSX - React</title>
  <script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
     ReactDOM.render(<h1>Hello, world!</h1>, document.getElementById('root'));
  </script>
</body>
</html>

Babel

次の所でTry it outをクリックするとJSXコードを変換しJavaScriptコードに変換して両者のコードを比較できる。

ReactDOM.render(<h1>Hello, world!</h1>, document.getElementById('root'));

上記のJSXのコードを入力すると以下のようにJavaScriptのコードに変換してくれる。

"use strict";

ReactDOM.render( /*#__PURE__*/React.createElement("h1", null, "Hello, world!"), document.getElementById('root'));

関数コンポーネント

Reactには関数コンポーネントとクラスコンポーネントがある。

See the Pen XWjJbZj by haku (@t-haku) on CodePen.

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>関数コンポーネント - React</title>
  <script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
     function App(props) {
        return <h1>function component - React</h1>;
     }
     ReactDOM.render(<App/>, document.getElementById('root'));
  </script>
</body>
</html>

14行目の<h1>は小文字から始まっているのでhtmlのタグだ。16行目の<App/>は大文字から始まっているのでコンポーネントだ。13行目にAppコンポーネントが定義されている。ここでは関数型のコンポーネントだ。11行目のidがrootの<div>に16行目の<App/>をレンダリングする。Appコンポーネントは<h1>を返すので表示されるのは<h1>だ。

Babelで変換後

"use strict";

function App(props) {
  return /*#__PURE__*/React.createElement("h1", null, "function component - React");
}

ReactDOM.render( /*#__PURE__*/React.createElement(App, null), document.getElementById('root'));

プロパティ

See the Pen jOMEPxN by haku (@t-haku) on CodePen.

コンポーネントのプロパティは関数型コンポーネントの場合引数のpropsに渡される。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>プロパティ - React</title>
  <script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
     function App(props) {
        return <h1>{props.text} - React</h1>;
     }
     ReactDOM.render(<App text="properties" />, document.getElementById('root'));
  </script>
</body>
</html>

Babelで変換後

"use strict";

function App(props) {
  return /*#__PURE__*/React.createElement("h1", null, props.text, " - React");
}

ReactDOM.render( /*#__PURE__*/React.createElement(App, {
  text: "properties"
}), document.getElementById('root'));

クラスコンポーネント

See the Pen eYdmNjz by haku (@t-haku) on CodePen.

クラスコンポーネントもthis.propsでプロパティを参照できる。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>クラスコンポーネント - React</title>
  <script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
     class App extends React.Component {
        render() {
          return <h1>{this.props.text} - React</h1>;
        }
     }
     ReactDOM.render(<App text="class component" />, document.getElementById('root'));
  </script>
</body>
</html>

Babel変換後

"use strict";

class App extends React.Component {
  render() {
    return /*#__PURE__*/React.createElement("h1", null, this.props.text, " - React");
  }

}

ReactDOM.render( /*#__PURE__*/React.createElement(App, {
  text: "class component"
}), document.getElementById('root'));

状態

See the Pen bGwNdjy by haku (@t-haku) on CodePen.

コンポーネントは状態を持つことができる。状態が変わるとレンダリングされる。Reactは仮想DOMを持っているので変更されていないところをレンダリングしないことでレンダリングコストを低減している。仮装DOMは表示が速いが、メモリを使う。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>状態 - React</title>
  <script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
     class App extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            text: '状態',
            number: 0,
          };
        }
        increment() {
          this.setState({number: this.state.number + 1});
        }
        render() {
          return (
            <div>
              <h1>{this.state.text}{this.state.number} - React</h1>
              <button onClick={() => this.increment()}>+</button>
            </div>
          )
        }
     }
     ReactDOM.render(<App />, document.getElementById('root'));
  </script>
</body>
</html>

Babelで変換後

"use strict";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      text: '状態',
      number: 0
    };
  }

  increment() {
    this.setState({
      number: this.state.number + 1
    });
  }

  render() {
    return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h1", null, this.state.text, this.state.number, " - React"), /*#__PURE__*/React.createElement("button", {
      onClick: () => this.increment()
    }, "+"));
  }

}

ReactDOM.render( /*#__PURE__*/React.createElement(App, null), document.getElementById('root'));

単方向データバインディング

See the Pen eYdmNPd by haku (@t-haku) on CodePen.

Reactのデータフローは単方向データバインディングなのでAppコンポーネントからMyButtonコンポーネントへpropsでデータを渡せるが、これはイミュータブル(immutable)なデータ、つまり変更できない。代わりに関数を渡すことでMyButtonのbuttonがクリックされたときにAppコンポーネントのincrement()関数を呼び出すことができる。

単方向データバインディングは双方向データバインディングよりもシンプルで高速となる。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>単方向データバインディング - React</title>
  <script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
     function MyButton(props) {
       return (
         <button onClick={props.onClick}>
           {props.name}{props.children}
         </button>
       );
     }
     class App extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            text: '単方向データバインディング',
            number: 0,
          };
        }
        increment() {
          this.setState({number: this.state.number + 1});
        }
        render() {
          return (
            <div>
              <h1>{this.state.text}{this.state.number} - React</h1>
              <MyButton name="+" onClick={() => this.increment()}>プラス</MyButton>
            </div>
          );
        }
     }
     ReactDOM.render(<App />, document.getElementById('root'));
  </script>
</body>
</html>

Babelで変換後

"use strict";

function MyButton(props) {
  return /*#__PURE__*/React.createElement("button", {
    onClick: props.onClick
  }, props.name, props.children);
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      text: '単方向データバインディング',
      number: 0
    };
  }

  increment() {
    this.setState({
      number: this.state.number + 1
    });
  }

  render() {
    return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h1", null, this.state.text, this.state.number, " - React"), /*#__PURE__*/React.createElement(MyButton, {
      name: "+",
      onClick: () => this.increment()
    }, "\u30D7\u30E9\u30B9"));
  }

}

ReactDOM.render( /*#__PURE__*/React.createElement(App, null), document.getElementById('root'));

コンポーネント

See the Pen qBaEdQV by haku (@t-haku) on CodePen.

関数コンポーネントでも状態は持てる。クラスコンポーネントよりシンプルになる。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <title>コンポーネント - React</title>
  <script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
     const { useState } = React
     function MyButton(props) {
       return (
         <button onClick={props.onClick}>
           {props.name}{props.children}
         </button>
       );
     }
     function App(props) {
        const text = 'コンポーネント';
        const [number, setNumber] = useState(0);
        return (
            <div>
              <h1>{text}{number} - React</h1>
              <MyButton name="+" onClick={() => setNumber(number + 1)}>プラス</MyButton>
            </div>
          );
     }
     ReactDOM.render(<App />, document.getElementById('root'));
  </script>
</body>
</html>

Babelで変換後

"use strict";

const {
  useState
} = React;

function MyButton(props) {
  return /*#__PURE__*/React.createElement("button", {
    onClick: props.onClick
  }, props.name, props.children);
}

function App(props) {
  const text = 'コンポーネント';
  const [number, setNumber] = useState(0);
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h1", null, text, number, " - React"), /*#__PURE__*/React.createElement(MyButton, {
    name: "+",
    onClick: () => setNumber(number + 1)
  }, "\u30D7\u30E9\u30B9"));
}

ReactDOM.render( /*#__PURE__*/React.createElement(App, null), document.getElementById('root'));