React.js を使ってみた

# はじめに

HTML＋JavaScript でアプリを作るのに、Vue.js を愛用してきました。
最近は React.js が流行のようです。使ってみることにしました。

# React.js とは

React は、ユーザインタフェース構築のためのJavaScriptライブラリである。シングルページアプリケーションやモバイルアプリケーションの開発におけるベースとして使用することができる。

[React - Wikipedia](https://ja.wikipedia.org/wiki/React)

# React.js を使ってみた

・React.js 18

## インストールする

React.js のアプリを作るには、Node.js を使ってローカルな開発環境を構築するのが一般的なようです。

[React 開発環境構築 - Qiita](https://qiita.com/rspmharada7645/items/25c496aee87973bcc7a5)

今回はあくまでも HTML ファイル一つで、ブラウザだけで動く React.js アプリを作ってみたいと思います。

[npmを使わずにReactの開発環境を構築する方法（オフィシャルサイトからダウンロード） | Syntax Error.](https://utano.jp/entry/2016/07/react-js-download-from-official-site/)
[npmを使わずにReactの開発環境を構築する方法（CDNで配信されているReactとbabel-coreを利用） | Syntax Error.](https://utano.jp/entry/2016/07/react-js-load-from-cdn/)

HTML ファイルに以下を追記します。

```html:
  <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
```

React.js を使うコードは、以下の `<script>` タグに記載します。

```html:
  <script type="text/babel">
```

## レンダリングする

HTML ファイルにはアプリの起点になるタグを用意します。

```html:
  <body>
    <div id="root"></div>
  </body>
```

起点になるタグに対して JavaScript のコードで HTML タグをレンダリングします。

```javascript:
    const root = ReactDOM.createRoot(document.querySelector('#root'));
    root.render(
      <p>Hello, React.js!</p>
    );
```

参考 [React入門チュートリアル (2) JSX | Hypertext Candy](https://www.hypertextcandy.com/react-tutorial-02-jsx)

ネットの入門記事を見ていると以下の書き方を多く見かけました。最新の React.js では前述の書き方がいいようです。

```javascript:
    const root document.querySelector('#root');
    ReactDOM.render((
      ........
    ), root);
```

参考 [React 18ではReactDOM.renderではなくcreateRootを使う | iwb.jp ](https://iwb.jp/react-18-uses-createroot-instead-of-reactdom-render/)

`render()` に記述する HTML タグでは、JavaScript のコードの変数や式を埋込できます。

```javascript:
    const name = "React.js!";

    const root = ReactDOM.createRoot(document.querySelector('#root'));
    root.render(
      <p>Hello, { name }</p>
    );
```

配列のデータを一覧表示することも可能です。

```javascript:
    const members = [
      { name: "John", instrument: "guitar" },
      { name: "Paul", instrument: "bass" },
      { name: "George", instrument: "guitar" },
      { name: "Ringo", instrument: "drums" }
    ];

    const root = ReactDOM.createRoot(document.querySelector('#root'));
    root.render(
      <div>
        { 
          members.map((member, index) =>
            <p key={ index }> { member.name } plays { member.instrument }.</p>
          )
        }
      </div>
    );
```

個人的には Vue.js の `<v-for>` の方が分かりやすく思いますね。

## コンポーネントを作成する

React.js は、独自のコンポーネントを作って利用することができます。

```javascript:
    class Element extends React.Component {
      render() {
        return <p>Hello, { this.props.name }</p>;
      }
    }

    const root = ReactDOM.createRoot(document.querySelector('#root'));
    root.render(
      <Element name="React.js" />
    );
```

独自タグを作れると言ってよさそうです。↑
呼出元から値を渡してコンポーネントで受取して使用できます。「属性」と言うそうです。

最近の React.js では、クラスを使うのでなく関数で定義するのがいいそうです。↓
「属性」は引数で受取ります。

```javascript:
    const Element = function(props) {
      return <p>Hello, { props.name }</p>;
    };

    const root = ReactDOM.createRoot(document.querySelector('#root'));
    root.render(
      <Element name="React.js" />
    );
```

「属性」を受取するところを書換えます。↓

```javascript:
    const Element = function({ name }) {
      return <p>Hello, { name }</p>;
    };
```

参考 [【React】コンポーネントの基本と使い方を初心者向けに解説！ | ちょいっぽ](https://choippo.com/react-component/)

## コンポーネントに状態を加える

コンポーネントは、呼出元から値を受取って使うだけでなく、内部で値を保持して使用します。

カウントアップするボタンのコンポーネントを作ってみます。↓

```javascript:
    const CountButton = function() {
      let count = 0;

      function addCount() {
        count += 1;
      };

      return (
        <button onClick={ addCount }>Count: { count }</button>
      );
    };

    const root = ReactDOM.createRoot(document.querySelector('#root'));
    root.render(
      <CountButton />
    );
```

`count` の値は変わるのですが、`{ count }` で表示される値は変わりません。再描画されないのです。
そこで React.js は、値を変えると同時に再描画させる仕組を用意しています。「状態」を管理すると言います。

```javascript:
    const CountButton = function() {
      const [ count, setCount ] = React.useState(0);

      function addCount() {
        setCount(count + 1);
      };

      以下略
```

`useState` で変数と、変数を更新するメソッドを用意しておきます。`useState` の引数は、変数の初期値です。

参考 [React入門チュートリアル (3) 属性と状態 | Hypertext Candy](https://www.hypertextcandy.com/react-tutorial-03-props-and-state)

用意する変数が配列のときは、どうでしょう。↓

```javascript
    const Element = function() {
      const [ items, setItems ] = React.useState([
        { id: 1, name: "Item1", amount: 1 },
        { id: 2, name: "Item2", amount: 1 },
        { id: 3, name: "Item3", amount: 1 },
      ]);

      function addItem(item) {
        items.push(item)  
      }

      function updateItem(index, item) {
        items[index] = { ...item };
      }

      function removeItem(index) {
        items.splice(index, 1);
      }

      以下略
```

以前にあるように、これでは再描画されません。
`useState` で用意したメソッドを使います。↓

```javascript:
      function addItem(item) {
        setItems([ ...items, item ]);
      }

      function updateItem(index, item) {
        setItems(items.map((item, n) => {
          if (n == index) items[index] = { ...item }; return item;
        }));
      }

      function removeItem(index) {
        setItems(items.filter((item, n) => (n != index)));
      }
```

参考 [useStateで配列要素を追加・削除・変更する方法 - Qiita](https://qiita.com/itachi/items/4184b2afc35b55b45568)

## イベントをハンドリングする

前述のコードで既に書いていますが、HTML のタグが受取ったイベントを処理することができます。

```javascript:

    const Element = function() {
      const [ name, setName ] = React.useState("React.js");

      function handleChange(e) {
        setName(e.target.value);
      }

      return (
        <div>
          <p>Hello, { name }</p>
          <input value={ name } onChange={ handleChange } />
        </div>
      );
    };
```

参考 [React入門チュートリアル (4) フォームとイベントハンドリング | Hypertext Candy](https://www.hypertextcandy.com/react-tutorial-04-form-and-events)

## コンポーネント間で状態を共有する

ルートのコンポーネントで用意した変数を、子供や孫のコンポーネントで使用したいときは、どうしたらいいでしょうか。

`useState` で用意した変数とメソッドを、「属性」としてコンポーネントに渡せばいいようです。↓

```javascript:
    const App = function() {
      const [ count, setCount ] = React.useState(0);

      return (
        <div>
          <Parent count={ count } setCount={ setCount } />
        </div>
      );
    };

    const Parent = function({ count, setCount }) {
      function addCount() {
        setCount(count + 1);
      }

      return (
        <div>
          <p>Parent</p>
          <button onClick={ addCount }>{ count }</button>
          <Child count={ count } setCount={ setCount } />
        </div>
      );
    };

    const Child = function({ count, setCount }) {
      function addCount() {
        setCount(count + 1);
      }

      return (
        <div>
          <p>Child</p>
          <button onClick={ addCount }>{ count }</button>
          <GrandChild count={ count } setCount={ setCount } />
        </div>
      );   
    };

    const GrandChild = function({ count, setCount }) {
      function addCount() {
        setCount(count + 1);
      }

      return (
        <div>
          <p>GrandChild</p>
          <button onClick={ addCount }>{ count }</button>
        </div>
      );
    };
```

渡したい変数やメソッドが多くなったり、親子の階層が深くなると、引数で渡すのは面倒です。
そこで React.js は、グローバルな「状態」を保持できる仕組を用意しています。

```javascript:
    const CountContext = React.createContext();

    const App = function() {
      const [ count, setCount ] = React.useState(0);

      return (
        <div>
          <CountContext.Provider value={{ count, setCount }}>
            <Parent />
          </CountContext.Provider>
        </div>
      );
    };

    const Parent = function() {
      const { count, setCount } = React.useContext(CountContext);
   
      中略    

          <Child />
        </div>
      );
    };

    const Child = function() {
      const { count, setCount } = React.useContext(CountContext);

      中略    
          <GrandChild />
        </div>
      );
    };

    const GrandChild = function() {
      const { count, setCount } = React.useContext(CountContext);

      中略    
    };
```

`useContext` で用意したオブジェクトを使います。

参考 [【React】useContextの使い方！初心者向けに分かりやすく解説！ | ちょいっぽ](https://choippo.com/react-hook-usecontext/)


## レンダリングのタイミングで実行する

レンダリングのタイミングで処理を実行したいことがあります。

```javascript:
    const App = function() {
      const [ visible, setVisible ] = React.useState(false);

      function toggleVisible() {
        setVisible(!visible);
      }

      return (
        <div>
          <button onClick={ toggleVisible }>表示を切替</button>
          <p>{ visible ? "true" : "false" }</p>
          <p>{ visible && <Child /> }</p>
        </div>
      );
    };

    const Child = function() {
      React.useEffect(function(){
        console.log("Hello, Child.");
      
        return(function(){
          console.log("Bye, Child.");
        });
      });

      return (
        <span>Child</span>
      )
    };
```

参考 [【React】useEffectの使い方！初心者向けに分かりやすく解説！ | ちょいっぽ](https://choippo.com/react-hook-useeffect/)

`userState` の使い所って何なのでしょう。

参考 [【保存版】「そのuseEffectの使い方あってる？」と言われる前に](https://zenn.dev/fujiyama/articles/c26acc641c4e30)

## チュートリアル

ここまで基本が分かると、以下のチュートリアルは容易に理解できると思います。

[チュートリアル：React の導入 – React](https://ja.legacy.reactjs.org/tutorial/tutorial.html)