📘

React・Typescriptってなに?何がすごい?

に公開

Reactとは

Javascriptのライブラリ。

※あくまでライブラリでありフルスタックなフレームワークではない
本格的なアプリを作るにはReactだけでは足りず、モジュールバンドラ、ビルドツール、ルーティング用パッケージ等の各種ツールを別途入れる必要がある

なぜこんなに採用されるのか?

  • 以下の特徴により、開発・保守が効率的になり、高いユーザ体験を提供できるようになるため。
  1. コンポーネント・アーキテクチャ
  2. クロスプラットフォーム
  3. 仮想DOM
  4. JSX

1. コンポーネント・アーキテクチャ

Reactはコンポーネントという独立した再利用可能なパーツを組み合わせることでアプリケーションを作るコンポーネントベース・アーキテクチャを採用している。これによりコードの分割や再利用が容易になる。また、コンポーネントの変更や編集をする際も、一つの変更に合わせて全てのコードを調整する必要がないためバグの修正や保守が楽になる。

以下は新入社員一覧を表示するアプリケーションのコード。
Alt text

  • App.tsx
    import type { FC } from 'react';
    import MemberList, { type Member } from './components/MemberLists';
    
    const App: FC = () => {
      const members: Member[] = [
        {
          id: '0001',
          name: '山田',
        },
        {
          id: '0002',
          name: '佐藤',
        },
        {
          id: '0003',
          name: '中本',
        },
        {
          id: '0004',
          name: '鈴木',
        },
      ];
    
      return <MemberList members={members} />;
    };
    
    export default App;
    
  • MemberLists.tsx
    import type { FC } from 'react';
    
    export interface Member {
      id: string;
      name: string;
    }
    
    type Props = {
      members: Member[];
    };
    
    const MemberList: FC<Props> = ({ members }) => {
      return (
        <>
          <h2>新入社員</h2>
          <ul>
            {members.map((member) => (
              <li key={member.id}>
                  {member.id}&nbsp;&nbsp;{member.name}
              </li>
            ))}
          </ul>
        </>
      );
    };
    
    export default MemberList;
    

<MemberList>というコンポーネントが外部で定義してあるため、このMemberListsを編集したとしても、App.tsxで何か合わせて編集する必要がない。これが「一つの変更に合わせて全てのコードを調整する必要がない」ということである。

2. クロスプラットフォーム

Reactは設計として本体と仮想DOMを実際の画面に反映するレンダラーが分離されている。
様々なドキュメントやプラットフォーム向けのレンダラーが提供されているため、コンポーネントベースなReactの書き方を学べば、レンダラーを変更することで様々なプラットフォームで開発ができる。
現在、React用の各種レンダラーで主要なものは以下の通りである。

  • React DOM ...... HTML DOM(公式標準パッケージ)
  • react-test-renderer ...... Javascriptオブジェクト(公式標準パッケージ)
  • React ART ...... HTML5 Canvas や SVG などのベクターグラフィック(公式標準パッケージ)
  • React Native ...... IOS および Android のネイティブアプリケーション
  • React Native for Windows + macOS ...... Winows および macOS のネイティブアプリケーション
  • React-pdf ...... PDFドキュメント
  • react-three-fiber ...... WebGLによる3Dグラフィック
  • React Unity ...... Unity 3D のUI
  • React Figma ...... Figmaオブジェクト
  • Ink ...... インタラクティブなコマンドラインアプリ

3. 仮想DOM

仮想DOMとは

オブジェクトツリーをメモリに保持しておいて、差分のあったところだけブラウザに再レンダリングさせることで実際のDOMと同期させる仕組みのこと。
Reactでは一度レンダリングを走らせるとReactElementのツリーと、それを展開したHTMLElementツリーが形成され、後者のHTMLElementツリーによって実際のDOMが形成される。

前者のReactElementのツリーのどこかでコンポーネントの状態が変更されると、新しい状態でそのコンポーネントが再実行され、前とは異なる内容のReactElementオブジェクトが返される。そしてそこから新しい内容のHTMLElementオブジェクトが形成される。
これら新旧のHTMLElementオブジェクトツリーを比較し、差分のあったところだけDOMを差し替えるようにブラウザに伝える。この仕組み全体のことを仮想DOMという。

仮想DOMを使うと何がうれしいのか

この仮想DOMを使った方式であれば、処理が走っても結果のDOMに差分が無ければ余分な再レンダリングが発生せず、差分があったとしても更新を最小限に抑えられる。

なお、React登場当時のブラウザはDOM更新が非常にオーバーヘッドの大きな処理であったため、仮想DOMの技術は画期的だった。しかし現在ブラウザや他のフレームワークの性能も上がっているので、2023年現在仮想DOMにおけるアドバンテージはなくなったともいえる。

4. JSXの利用

JSX(JavaScript XML)とは、Javascriptのシンタックスシュガーのこと。Reactでは、UIを記述するために使われる。

<MyComponent foo="bar">baz</MyComponent>                    //JSX
 ↓
React.createElement('MyComponent', {foo: 'bar'}, 'baz');   //React.createElementのメソッドコール
 ↓
{                                                          //ReactElementオブジェクト
    type: 'MyComponent',
    props: {
        foo: 'bar',
        children: 'baz'
    },
    key: null,
}
  ↓
HTMLElementオブジェクト

JSXを使うと何がうれしいのか

  • 現代のWebシステムにおいて、ロジックとマークアップは密に関係している。ユーザの操作で状態が変わり、ロジックが走り、コンポーネントの見た目もそれによって動的に変わるということである。
  • JSXでは、UIのロジックとマークアップを同じ場所に書くことになる。これにより、コンポーネントの独立性が高まり、コードの安全性も高まる。
  • JSXはJavascriptの純粋な式であり、レンダリングもJavascriptの枠組みのなかでおこなう。これにより、Vue.jsのようなテンプレート構文のフレームワークに比べ独自構文が少なくすむというのもメリットとして挙げられる。

Typescriptとは

  • 型構文をもつJavascript(AltJs)
  • コンパイルするとJavascriptになる

なぜTypescriptを使うのか?

  • 以下の特長を持ちつつ、Javascriptの構文とほとんど同じという利便性により広く使われるようになった
  1. 静的型付け
  2. 型推論
  3. Null安全性
  • これら特長は大規模開発に向いており、同じく大規模開発のために開発されたReactと組み合わせるのに都合がよい
  • ちなみに開発元はMicrosoftで、C# や .NET Frameworkの設計者であるAnders Hejlsbergが最初期から開発に関わっている

1.静的型付け

「コンパイル時」に型が決まること。
Pythonのような動的型付け言語は、実行時に型が決まるためプログラミングの自由度が高まる一方、エラーの発見が遅れてしまう。

def minus(x):
  return x - 1
print(minus("two"))

実行結果:

Traceback (most recent call last):
  File "Main.py", line 6, in <module>
    print(minus("two"))
  File "Main.py", line 5, in minus
    return x - 1
TypeError: unsupported operand type(s) for -: 'str' and 'int'

一方Typescriptのような静的型付け言語は、コンパイル時に型が決まるので明らかにおかしな代入を早期発見でき、バグの防止につながる。
例えば、以下のような代入を行うとコンパイル時にエラーとなる。

function minus(x: number){
  return x - 1; 
}

minus("two");

また統合開発環境やVSCodeなど高機能エディタを使えばコンパイルせずともコーディング中にリアルタイムで問題個所を指摘してくれるため、バグを防ぎやすくなる。
Alt text

2. 型推論

型を明示的に示さなくても、コンパイラが文脈から型を推測してくれること。

let x = 1;
x = "hello";

上のコードは型を宣言していないが、これをVSCodeで記述すると以下のように怒られる。またlet x: numberにように型を推測してくれていることが分かる。これが型推論である。
Alt text

3. Null安全性

コンパイラオプションstrictNullChecksを有効にすることで、nullundifinedの代入を禁止できる。

{
  ⋮
  "strictNullChecks": true,
}

strictNullChecksを有効にした後、以下のようなコードを書くと怒られる。

const foo: string = null;
const bat: number = undefined;

Alt text
Alt text

実際に実行してもエラーとなる。

$ ts-node
> .load test.ts
const foo: string = null
const bar: number = undefined

<repl>.ts:4:7 - error TS2322: Type 'null' is not assignable to type 'string'.

4 const foo: string = null
        ~~~
<repl>.ts:5:7 - error TS2322: Type 'undefined' is not assignable to type 'number'.

5 const bar: number = undefined
        ~~~

参考

Discussion