Reactで開発するにはコンポーネントについて理解する必要があります。
本記事では、Reactの関数コンポーネントやクラスコンポーネントについて具体的なコードを交えて解説してきます。
関数(Functional)コンポーネント
関数型コンポーネントは、シンプルで、純粋に機能的で、ロジックをつけるのがとても簡単な書き方ができます。
以下は、いくつかの型付けされたプロパティを持つ関数型コンポーネントの例である。
import React from 'react';
type CardProps = {
title: string,
paragraph: string
}
export const Card = ({ title, paragraph }: CardProps) => <aside>
<h2>{ title }</h2>
<p>
{ paragraph }
</p>
</aside>
const el = <Card title="Welcome!" paragraph="To this example" />
TypeScriptはなるべく使わないようにします。プロパティに型を作成し、機能コンポーネントのパラメータがその型であることをTypeScriptに伝えます。
いくつかのプロパティをオプションにしたい場合は、それぞれのPropsタイプで行います。
type CardProps = {
title: string,
paragraph?: string // paragraphはオプショナル
}
使えるジェネリクス型があります。
import React, { FunctionComponent } from 'react'; // importing FunctionComponent
type CardProps = {
title: string,
paragraph: string
}
export const Card: FunctionComponent<CardProps> = ({ title, paragraph }) => <aside>
<h2>{ title }</h2>
<p>
{ paragraph }
</p>
</aside>
const el = <Card title="Welcome!" paragraph="To this example" />
関数のパラメータは、汎用的なFunctionComponentから推論しています。
それ以外は、最初の例と非常によく似ています。しかし、オプションの子コンポーネントを使用することができます。
type CardProps = {
title: string,
paragraph: string
}
// we can use children even though we haven't defined them in our CardProps
export const Card: FunctionComponent<CardProps> = ({ title, paragraph, children }) => <aside>
<h2>{ title }</h2>
<p>
{ paragraph }
</p>
{ children }
</aside>
クラスコンポーネント
私がReactを使うことに納得したもののひとつに、関数コンポーネントがあります。コンポーネントの「古いやり方」は、クラスコンポーネントです。
そして、クラスごとにステートを保持することができます。ステートはpropsのようなものだが、プライベートで、コンポーネントだけがコントロールできます。
もちろん、@types/react typingsはそれらを完全にサポートしており、また同様に使いやすい。
クラスコンポーネントは、ベースとなるReact.Componentクラスから拡張する必要があります。タイピングはジェネリックでこのクラスを強化し、プロップ(先ほどのFunctionComponentのようなもの)やステートを渡します。時計コンポーネントを作ってみましょう
import React, { Component } from 'react'; // let's also import Component
// the clock's state has one field: The current time, based upon the
// JavaScript class Date
type ClockState = {
time: Date
}
// Clock has no properties, but the current state is of type ClockState
// The generic parameters in the Component typing allow to pass props
// and state. Since we don't have props, we pass an empty object.
export class Clock extends Component<{}, ClockState> {
// The tick function sets the current state. TypeScript will let us know
// which ones we are allowed to set.
tick() {
this.setState({
time: new Date()
});
}
// Before the component mounts, we initialise our state
componentWillMount() {
this.tick();
}
// After the component did mount, we set the state each second.
componentDidMount() {
setInterval(() => this.tick(), 1000);
}
// render will know everything!
render() {
return <p>The current time is {this.state.time.toLocaleTimeString()}</p>
}
}
そして、適切なツール化によって、大量のインフォを手に入れることができるのです。
defaultProps
デフォルトプロパティは、プロパティにデフォルト値を指定するためのものです。すべての値を明示的に設定する必要がない場合に使用します。Reactでは、defaultPropsというプロパティがコンポーネント用に用意されています。
TypeScriptはバージョン3.0からdefaultPropsを尊重するようになりました。最新のReactの型付け(v 16.4.8)であれば、すぐにでも使えるでしょう。
import React, { Component } from 'react';
type NoticeProps = {
msg: string
}
export class Notice extends Component<NoticeProps> {
static defaultProps = {
msg: 'Hello everyone!'
}
render() {
return <p>{ this.props.msg }</p>
}
}
FunctionComponentsについては、ES6のデフォルト値構文とオプションのタイププロパティを使用することをお勧めします。
type CardProps = {
title: string,
paragraph?: string // the paragraph is optional
}
// No need to define the defaultProps property
export const Card: FunctionComponent<CardProps> = ({ title, paragraph = 'Hello World' }) =>
<aside>
<h2>{ title }</h2>
<p>
{ paragraph }
</p>
</aside>