Closed1

React + TypeScript

high-ghigh-g

モチベーション

  • 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()
   }
 }

参考

https://qiita.com/sunnyG/items/05c2e9381d6ba2d9fccf

このスクラップは2022/09/15にクローズされました