React + TypeScript
モチベーション
- React + TypeScriptの構成で書けるようになる
環境構築
1.npx create-react-app . でGitリポジトリにreactの環境構築
2.yarn add typescript @types/react @types/react-dom でtypescriptと型定義ファイルをインストール
ここまでやると、import 文の型エラーは消えるもののなにかエラーが表示されている
諦めて、npx create-react-app . --template typescript で作ろう
できた。
react-app-env.d.tsがなかったのかな?
index.tsxやApp.tsxの記述はかわらない。
↓は何だろ?
トリプルスラッシュディレクティブの記述で、<reference types="react-scripts" />と記述することで、
importのエラーをなくすためっぽい。
/// <reference types="react-scripts" />
一旦、触ってみる。
Props
index.tsx
ReactDOM.render(
<React.StrictMode>
<App message="Hello, React!" />
</React.StrictMode>,
document.getElementById('root')
)
App.tsx
function App({ message }) {
return <div className="App">{message}</div>
}
エラーが出てる。
Binding element 'message' implicitly has an 'any' type.
messageは暗黙のanyを持つ
what?
一旦、下記のようにする
App.tsx
function App(props) {
return <div className="App"></div>
}
Parameter 'props' implicitly has an 'any' type.
propsの型指定できていない。
一旦、anyとすると、エラーは消えるが、別の対応が必要。
function App(props: any) {
const { message } = props
return <div className="App">{message}</div>
}
export default App
↓のように変更。これで他のpropsは渡せなくなり、さっきよりも堅牢になるが、まだ違う。
function App({ message }: { message: string }) {
return <div className="App">{message}</div>
}
export default App
↓一般的には、この様に記述する。
interface AppProps {
message: string
}
function App({ message }: AppProps) {
return <div>{message}</div>
}
export default App
interfaceを用いて記述する。
もう少しFunctionコンポーネントの型アノテーションについて確認する。
上の関数系からアロー関数の形に変更。
より、現実的な書き方
interface AppProps {
message: string
}
// 型引数にAppPropsを渡す
const App: React.FunctionComponent<AppProps> = ({ message }) => {
return <div>{message}</div>
}
export default App
デフォルト値
interface AppProps {
message?: string
}
const App: React.FC<AppProps> = ({ message }) => {
return <div>{message}</div>
}
// React.FunctionComponent型やAppProps型が定義できていないとデフォルト値が定義できない
App.defaultProps = {
message: 'Hello, defaultProps!!',
}
export default App
useState
import { useState } from 'react'
const Counter: React.FC<{}> = () => {
const [value, setValue] = useState<number>(0) // useState<型>(初期値)
const clickHandler = (num: number): void => {
setValue(value + num)
}
return (
<div>
<p>value: {value}</p>
<button
onClick={() => {
clickHandler(1)
}}
>
+1
</button>
<button
onClick={() => {
clickHandler(-1)
}}
>
-1
</button>
</div>
)
}
export default Counter
useRef
useStateの様な使い方
const renderTimes = useRef<number>(0)
useEffect(() => {
renderTimes.current = renderTimes.current + 1
})
DOMを扱う場合
VSCodeで、ref={}が記述されている箇所のrefにマウスをあてると、型の説明が表示される。
↓は、インプット要素を記述したケース
const inputEl = useRef<HTMLInputElement>(null!) // nonNullAssertionOperator (nullだけど、nullじゃないよ!!)
const focusInput = () => {
inputEl.current.focus() // もしくは、inputEl.current?でoptional chainingをつける
}
useReducer
type StateType = { count: number }
const initialState: StateType = { count: 0 }
type ActionType = {
type: 'decrement' | 'increment'
}
function reducer(state: StateType, action: ActionType): StateType | never {
switch (action.type) {
case 'increment':
return { count: state.count + 1 }
case 'decrement':
return { count: state.count - 1 }
default:
throw new Error()
}
}
参考