【環境構築】LaravelでReactとTypescriptを使う方法

今回はLaravelでreactとTypescriptを使えるように開発環境を構築していきます。
初学者なので2週間ぐらいかかってしまいましたが誰かの参考になれば幸いです。

#開発環境

Docker 20.10.5
docker-compose 1.28.5
php 7.3
laravel 6.20.26
Composer 2.0.8
MySQL 8.0
nginx 1.15.12-alpine

Dockerを使っていますがDockerの環境構築は省略させていただいてコンテナ内でlaravelをインストールするところから始めます。

#Laravel＆Reactの環境を整える

はじめにappコンテナに入りLaravelをインストールします。

```
# composer create-project --prefer-dist "laravel/laravel=6.*"
```

<br>
続いてLaravel/uiをインストール。
最新版のLaravel/uiをインストールするとLaravel6ではエラーが出るのでバージョン指定をしています。

```
# composer require laravel/ui:^1.0 --dev
```

<br>
Reactのスカフォールドをインストール。ついでに認証機能も追加しました。

```
# php artisan ui react --auth
```

<br>
webコンテナに入りnpmでインストールとビルドをします。(Dockerfileでnodeの設定済み)

```
# npm install & npm run dev
```

ここまででローカルサーバーにアクセスしたらちゃんとLaravelのwelcome.blade.phpのビューが表示されていたら成功です。

#Laravel Mixを用いてTypescriptの環境を構築する

webコンテナに入りnpmコマンドでTypescriptで必要なパッケージをインストールします。
ついでにreact-router-domもこのタイミングでインストール。

```
# npm install ts-loader typescript react-router-dom @types/react @types/react-dom @types/react-router-dom --save-dev
```

<br>
### webpack.mix.jsを編集
TypeScriptとSCSSでビルドできるようにwebpack.mix.jsを編集します。


```js:webpack.mix.js
const mix = require('laravel-mix');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.ts('resources/ts/index.tsx', 'public/js')
   .sass('resources/sass/app.scss', 'public/css')
   .version(); 
```

reactをインストールしているのでデフォルトではおそらくmix.react()になっていると思いますがtypescriptをビルドするのでmix.ts()に変更します。
また、.version()を追加しておかないと`npm run dev`をしてもキャッシュが残っていて変更が反映されないことがあるので一応記述します。

### tsconfig.jsonを作成

webコンテナ内で以下のコマンドを実行してtsconfig.jsonを作成します。

```
./node_modules/.bin/tsc --init
```

<br>
オプションが多いので適宜確認するしかないかと...
地味にエラーがよく出るのでエラー内容確認して適宜修正しました。

```json:tsconfig.json
{
  "compilerOptions": {
      "outDir": "./built/",
      "sourceMap": true,
      "strict": true,
      "noImplicitReturns": true,
      "noImplicitAny": true,
      "module": "es2015", //フロント使用予定なのでcommonjsは使用しません
      "jsx": "react", // tsxファイルをjsxやjsにコンパイルする際の出力の形式を指定する
      "experimentalDecorators": true,
      "emitDecoratorMetadata": true,
      "moduleResolution": "node",
      "target": "es6",
      "lib": [
          "es2016",
          "dom"
      ],
      "allowSyntheticDefaultImports": true // エクスポートしないモジュールからのインポートを許可する。これでtypescriptでreactをインポートするときにimport React from 'react';を使えるようになる
  },
  "include": [
      "resources/ts/**/*" // TypeScriptのソース配置場所
  ]
}
```

#tsxファイルを作成して表示されるか確認

tsxファイルが正しく表示されるか確認します。
まず、welcome.blade.phpを以下のように編集します。

```php:resouces/views/welcome.blade.php
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Laravel</title>
        <link rel="stylesheet" href="{{ mix('css/app.css') }}" />
    </head>
    <body>
        <div id="app"></div>
    </body>
    <script src="{{ mix('/js/index.js') }}"></script>
</html>
```

次にresources配下にtsフォルダを作成し、その中にindex.tsxを作成します。
index.tsxは以下のように記述してください。

```tsx:index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
 
const App: React.FC = () => {
    return (
        <div>
            <p>こんにちは</p>
        </div>
    )
}
 
if (document.getElementById('app')) {
    ReactDOM.render(<App />, document.getElementById('app'));
}
```

そしてもう一度`npm run dev`を実行しましょう。

```
# npm run dev
```

このままローカルサーバーで.index.tsxが表示されればいいのですが、僕の場合は`TypeError: loaderContext.getOptions is not a function`というエラーが出ていました。
これはlaravelのバージョン(今回は6系を使用)に対してts-loaderのバージョンが新しすぎるために出るエラーのようです。

ts-loaderを一度アンインストールして古いバージョンを入れましょう。

```
# npm uninstall ts-loader
# npm install ts-loader@8.2.0 --save-dev
```

これで最後にもう一度`npm run dev`を実行しましょう。

```
# npm run dev
```

![0215c84b3c4c509e0985baad528ed419.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474463/abcc0760-8f21-61bc-e94d-ed1110e09a27.png)