注: 趣味グラマーです。
質は保証しかねます。
作りたいもの
既にTypescriptで作成済の重たい計算を行うライブラリがあります。
この計算の入力の作成、実行、出力の表示のすべてをフロントエンドで行えるPWAを作りたい・・・
以下のライブラリ等を利用して作成します。
- 本体: CRA
- 入出力の管理: Redux(redux-toolkit)
- UI: Material-UI
- 出力のグラフ化: Victory
- 重たい計算: Web Worker API
- ホスティング: Netlify
情報ソース
TypeScript+Material UI v4
バージョン4の書き方のお手本
TypeScript + Material-UI v4 のスタイル付きコンポーネント作成ガイド - Qiita
redux-toolkit (Javascript)
reduxの基本からredux-toolkit
の使い方まで
HookとRedux ToolkitでReact Reduxに入門する | Hypertext Candy
Typescript + redux-toolkit
redux-toolkit
公式
Usage With TypeScript | Redux Toolkit
CRA+Web Worker
workerize-loader
を導入する方法です。
eject
しなくてもいけちゃいます。
Integration with Create React App · Issue #5 · developit/workerize-loader · GitHub
React + TypeScriptで開発するときのコツ
型が分からない問題の具体的な解決策など。
https://www.youtube.com/watch?v=Z5iWr6Srsj8
Redux関連で発生した問題と解決策
store
を作ってProviderで渡すところまでは、2つめの情報ソースでTypeScriptだろうと関係なくできました。
その後に発生した問題です。
useSelector
のところでstore
の型が不明
3つめ情報ソースであるredux-toolkit
公式ドキュメントの一番上に書いてありました。
// sliceを定義しているファイル const counterSlice = createSlice({ name: "counter", initialState: 0, reducers: { // ... } }); export default counterSlice; // reducerを定義しているファイル import counterSlice from '上のファイル'; const rootReducer = combineReducers({ counter: counterSlice.reducer, // ... }); export type RootState = ReturnType<typeof rootReducer>; // useSelectorするファイル(comsumer) import { RootState } from '上のファイル'; const YourComponent = () => { const count = useSelector((state: RootState) => state.counter); return <div>{count}</div> }
独自定義型のstate
を持つslice
のreducer
がコンパイルエラー
slice
内でinitalState
をきちんと型定義しておくと、reducer
ではかなり型推論がきき、全く型を書く必要がなくなりました。
type Aaa = { bbb: number, ccc: string } const initalState = { bbb: 5, ccc: "f*ck" } const aaaSlice = { name: "aaa", initialState, reducers: { actionA: (state, action) => { return {...state, bbb: action.payload} } } }
action
の型をきちんと定義したい
独自の型のPayloadを渡すとき、コンパイルが通らなくなります。
PayloadAction
型を使います。
こちらもredux-toolkit
公式ドキュメントがソースです。
import { createSlice, PayloadAction } from "@reduxjs/toolkit"; type Aaa = { bbb: number, ccc: string } const initalState = { bbb: 5, ccc: "f*ck" } const aaaSlice = { name: "aaa", initialState, reducers: { setAll: (state, action: PayloadAction<Aaa>) => { return action.payload } } }
Web Worker関連
かなり大変でした。
CRAでworker-loader
は将来的にサポートされる予定ですが、どんどん先延ばしにされて行っています。
https://github.com/facebook/create-react-app/pull/5886
サポートされていない以上、CRA
のお庭の外に出て(eject
して)戦うしかありません。
正直Webpackは勉強したくないし、事故ったっときに解決できる気がしないので、eject
は眼中にありませんでした。
水辺でパチャパチャしてるくらいが今の私には丁度いいのです。
そんな中で上記のソースをみつけ、一瞬で実装できました。
sampleWorker.worker.ts
内のfoo
の下に自分が使う関数を定義しただけです。
使い方もComponent.tsx
とほぼ同じです(async/awaitを使っただけ)。