TypeScriptの基礎に関してはこちらの記事も参考にしてください。
さらっとTypeScript入門
環境構築
node.jsは入っていることとします。今回はcreate-react-appで始めます。
TypeScriptの場合はオプションに--typescript
を指定します。
$ npx create-react-app react-lesson --typescript
インストールが終わったら。下記を実行します。
$ cd react-lesson $ npm start
ブラウザが起動して何か表示されたら成功です!
初期画面をカスタマイズしてみよう
最初に表示される画面をカスタマイズしてみます。
プログラムファイルはsrc
ディレクトリに入っています。
最初に読み込まれるファイルはindex.tsx
ですが、そこからApp.tsx
を読み込んで表示する設定をしているので、App.tsx
を下記のように編集してみましょう。
src/App.tsx
import React from 'react' const App: React.FC = () => { return ( <div className="App"> <p>ハロー React!!</p> </div> ) } export default App
「ハロー React!!」と表示されましたか。おめでとうございます。
AppにReact.FCという型を指定してますが、これは関数コンポーネント(FunctionComponent)のことです。
Reactはこの関数コンポーネントのreturnにHTMLタグ(のようなものを)を書くことでそのまま表示することができます。
これをJSXと呼びますが、TypeScriptなのでTSXです。
Reactコンポーネントはクラスベースの作成方法もありますが、今後は関数コンポーネントで書くことが推奨されているらしいので、基本的にはこちらを使うといいと思います。
TSXで変数の表示
コンポーネントの中に変数を定義してTSXで表示してみましょう。
TSXで変数を表示する場合は変数を波括弧( {} )で囲みます。
const App: React.FC = () => { const message: string = 'こんにちは React!!' return ( <div className="App"> <p>{ message }</p> </div> ) }
オブジェクト配列をリスト表示
Webアプリケーションを作っているとオブジェクトの配列を扱うことはよくありますよね。
その場合は最初にオブジェクトのタイプを作ります。
type Item = { id: number title: string }
作成したタイプの配列型を指定して配列を作ります。
const items: Item[] = [ { id: 1, title: '一番高い商品' }, { id: 2, title: '一番ださい商品' } ]
あとはmapで一つずつ表示しましょう。
const App: React.FC = () => { return ( <div className="App"> <ul> { items.map((item: Item) => ( <li key={item.id}>{ item.title }</li> )) } </ul> </div> ) }
コンポーネントを分ける
Reactのアプリケーションは複数のコンポーネントを組み合わせて作成することになります。
今はAppコンポーネントしかないので、Child
という子のコンポーネントを作成して、Appコンポーネントに配置してみましょう。
const Child: React.FC = () => { return ( <p>子コンポーネント</p> ) } const App: React.FC = () => { return ( <div className="App"> <Child /> </div> ) }
子のコンポーネントにデータを渡す
先ほどの例は固定された文字列を表示するだけの子コンポーネントでしたが、親から表示する文字列を指定できるようにしてみましょう。
ReactではこのようなときはProps
という機能を使います。
TypeScriptの場合はどのような値を使うのかをPropsのタイプで作成してから、React.FCのジェネリクスで指定します。
type Props = { message: string } const Child: React.FC<Props> = props => { return ( <p>{ props.message }</p> ) } const App: React.FC = () => { return ( <div className="App"> <Child message="子のコンポーネントに渡す" /> </div> ) }
記述量は増えますが、VSCodeなどTypeScriptがサポートされているエディターを使えば、補完されるので結構さくさく書けると思います。
タグで囲んだデータを子のコンポーネントに渡す
Propsはタグ属性のような書き方で値を渡しましたが、children
を使うと、値をタグで囲んで渡すことができます。
type Props = { children: React.ReactNode } const Child: React.FC<Props> = props => { return ( <p>{ props.children }</p> ) } const App: React.FC = () => { return ( <div className="App"> <Child>子のコンポーネントに渡す</Child> </div> ) }
Propsの値が多くなるとたくさんprops書くの大変ですよね。分割代入を使うとスマートに書けます。
type Props = { number: string children: React.ReactNode } const Child: React.FC<Props> = ({ number, children }) => { return ( <p>{ number }の{ children }</p> ) } const App: React.FC = () => { return ( <div className="App"> <Child number="1">子コンポーネント</Child> <Child number="2">子コンポーネント</Child> </div> ) }
ファイルを分ける
コンポーネントを分けたらファイルも分けたくなりますね。
Reactの使い方というわけではありませんが、別ファイルのコンポーネントをインポートして使ってみましょう。
export defaultの場合
export default
の場合はimportで変数に代入するように使います。
src/components/Child.tsx
import React from 'react' const Child: React.FC = () => { return ( <div>子のコンポーネント</div> ) } export default Child
src/App.tsx
import Child from './components/Child' const App: React.FC = () => { return ( <div> <Child /> </div> ) } export default App
exportだけの場合
ファイルに複数exportがある場合はdefaultは付けないで次のようにします。(一つはdefaultにできます。)
src/components/Child.tsx
import React from 'react' export const Child1: React.FC = () => { return ( <div>子のコンポーネント1</div> ) } export const Child2: React.FC = () => { return ( <div>子のコンポーネント2</div> ) }
インポートするときは分割代入を使います。
src/App.tsx
import { Child1, Child2 } from './components/Child' const App: React.FC = () => { return ( <div> <Child1 /> <Child2 /> </div> ) } export default App
ステートを使った状態管理
変数の値を更新すると、表示している値も同時に変更されるというのがReactを使う利点の一つです。
以前はクラスコンポーネントで作りステートという機能を使って状態管理をしましたが、関数コンポーネントでもHooksという機能を使うことで状態管理できるようになりました。今後はこちらがスタンダートになると思うのでこちらを使っていきましょう。
Hooks APIを使用した状態管理はuseState
を使用します。
ステートの値が数値の場合は次のように記述します。
const [ count, setCount ] = useState<number>(0)
useState
は配列の分割代入で2つの変数に入れます。
一つ目の変数(count)はステートを表示する変数です。
二つ目の変数(setCount)はステートの値を更新する為の関数です。
このステートの表示と更新するボタンを作成してみましょう。
import React, { useState } from 'react' const App: React.FC = () => { const [ count, setCount ] = useState<number>(0) return ( <div className="App"> <div>{ count }</div> <div> <button onClick={() => setCount(count+1)}>+1</button> <button onClick={() => setCount(count-1)}>-1</button> </div> </div> ) }
以前のクラスコンポーネントと比べると、だいぶスマートに書けるようになりましたね。
ただ、これだと描画するたびに関数が再定義されてパフォーマンスがよくないようなので、次のようにしましょう。
import React, { useState, useCallback } from 'react' const App: React.FC = () => { const [ count, setCount ] = useState<number>(0) const handleIncrement = useCallback(() => { setCount(prev => prev + 1) }, []) const handleDecrement = useCallback(() => { setCount(prev => prev - 1) }, []); return ( <div className="App"> <div>{ count }</div> <div> <button onClick={handleIncrement}>+1</button> <button onClick={handleDecrement}>-1</button> </div> </div> ) }
reactでuseStateとuseCallbackを使う
実践TypeScript ~ BFFとNext.js&Nuxt.jsの型定義~
動画版はこちら