車輪の二度漬け

railsを触っていましたが、最近はreactに興味あります。

LoLで出会ったプレイヤーを忘れないアプリを作った

ちょっと前にReact勉強のために作り始めて、その後、社内で勉強会のネタに使ったあとそのまま放置していたが、コードだけ整えて一旦公開することにした。

作ったもの

スラム街として有名なLeague of Legends(LoL)で出会ったステキなプレイヤーをメモとともに記録できるアプリ。

GitHub - Ryu-u/remember-that-guy

これから追加したい機能

  • 本番対応
  • 認証機能を追加して多人数対応
  • 例外処理

今回の学び

react-exhaustive-depsのwarning

副作用フックの利用法 – React useEffectについてのこのセクションの補足に

もしも副作用とそのクリーンアップを 1 度だけ(マウント時とアンマウント時にのみ)実行したいという場合、空の配列 ([]) を第 2 引数として渡すことができます。

とあるので、てっきりuseCallbackの第2引数についても同じだと思ってたから、初回以降変わる必要のない値についてはuseCallbackの第2引数に[]を渡していた。
ただ、そうすると、react-exhaustive-deps

React Hook React.useCallback has a missing dependency: 'setIsModalOpen'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

というwarningを吐くのでおかしいなと思ってたんだけど、リファレンスをよく読んだら、useCallbackの補足

依存する値の配列はコールバックに引数として渡されるわけではありません。しかし概念としては、この記法はコールバックの引数が何なのかを表現しています。コールバックの内部で参照されているすべての値は依存の配列内にも現れるべきです

と書いてあった。
useEffectuseCallbackでは第2引数の意味合いが少し違うのね。

ちなみに、useCallbackを使ったコードは https://github.com/Ryu-u/remember-that-guy/blob/master/src/components/past-games/PastGames.tsx#L21 https://github.com/Ryu-u/remember-that-guy/blob/master/src/components/past-games/DescriptionModal.tsx#L22
この2箇所なんだけど、今考えてみるとどちらもuseCallbackを使う必要がなかった...。
あとで修正しておこう。

フロントエンドとバックエンドで使用パッケージを共有する

正直、これはSPAの本来の用途とはズレているはずであまりいい方法ではないと思う。 ただ、今回はcreate-react-appとExpressをTypeScriptを使って開発する際に、型をフロントエンドとバックエンドで共有したり、そもそもマイクロサービス的に複数のサーバーを組み合わせたりしないのでnode_modulesが2つに分かれるのが嫌だなと思って、使用パッケージを共有することにした。

(此処にVSCodeのキャプチャ)

この構成にするために工夫するところは3つある。 まずはpackage.json

  "scripts": {
    "frontend-start": "BROWSER=firefox react-scripts start",
    "backend-start": "nodemon",
    "start": "ts-node --project tsconfig.backend.json backend/index.ts",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "heroku-postbuild": "yarn && yarn run build "
  }

frontend-startwebpack-dev-serverの立ち上げ。 backend-startでnodemonを使って、バックエンドのソースを更新した時に自動でコンパイルを再実行させ流ような形でExpress起動(後述するがnodemonではyarn startを実行している)。 他はcreate-react-appが作るものとかHeroku向けのコマンドとか。 Heroku向けのコマンドはまだ動かしてないので間違ってるかも。

次に、nodemonの設定はこういう感じ。

{
  "watch": ["backend"],
  "ext": "ts",
  "exec": "yarn run ts-node --project tsconfig.backend.json -r dotenv/config backend/index.ts"
}

バックエンド用のファイルを格納しているbackend/ディレクトリを監視し、その中のソースが変更されたら再コンパイルする感じにしてる。

そして最後に、tsconfig.jsonの設定。
今回はルートディレクトリにフロントエンド用のtsconfig.jsonとバックエンド用のtsconfig.backend.jsonを作成した。
tsconfig.jsonを分けた理由は、フロントエンドとバックエンドでコンパイル後のJSのバージョンをフロントエンド→ES5、バックエンド→ES6という形にしたかったから。 それぞれのtsconfig.jsonはこちら

# フロントエンド用tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve"
  },
  "include": ["src", "src/types"]
}
# バックエンド用tsconfig.backend.json
{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "module": "commonjs",
    "esModuleInterop": true,
    "target": "es6",
    "noImplicitAny": true,
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "dist",
    "baseUrl": ".",
    "paths": {
      "*": ["node_modules/*", "src/types/*"]
    }
  },
  "include": ["backend/**/*"]
}

正直重要なのがimportを使うためのallowSyntheticDefaultImportsesModuleInteropくらいしかわかってないので、このあたりはまたどこかのタイミングでさいきょうのtsconfig.jsonを書きたい。

所感

はじめは認証機能を追加するつもりはなく勉強だし一人用でいいやと思っていたのだが、思いのほか簡単に作れてしまったので、認証機能を追加しようと考え直した。
で、前から少し気になってたReactでのOAuth認証をやろうとしたところ、これが全くうまくいかない...
色々ググってみて紹介されているやり方を片っ端から試してみたが全然ダメ。
なので、これは後でまたチャレンジすることにして、コードだけ整えつつ今の状態で公開して一区切りつけることにした。
ReactでのOAuth認証は後でまた試して記事を書きたい。
あと、最近はLoLをやらなくなってしまった。