クラスコンポーネント
TypeScript の中では、React.Component
はジェネリック型(別名 React.Component<PropType, StateType>
)なので、(オプションの)小道具とステートの型の parameta を指定する必要があります。
type MyProps = {
// using `interface` is also ok
message: string;
};
type MyState = {
count: number; // like this
};
class App extends React.Component<MyProps, MyState> {
state: MyState = {
// optional second annotation for better type inference
count: 0,
};
render() {
return (
<div>
{this.props.message} {this.state.count}
</div>
);
}
}
これらの型/インターフェースを再利用のためにエクスポート/インポート/拡張できます。
state
を 2 回アノテーションする理由
state
のクラスプロパティを明示的にアノテーションする必要はありませんが、this.state
へのアクセス時とステートの初期化時の型の推論が容易になります。
これらは 2 つの異なる動作を行うからです。2 番目のジェネリック型パラメータにより、this.setState()
は正しく機能します。このメソッドは基本クラスから派生していますが、コンポーネント内で state
を初期化すると基本的な実装がオーバーライドされるため、コンパイラに実際には異なることは何も行っていません。ということを確認する必要があります。
readonly
は必要ありません
サンプルコードには、小道具とステートを不変にするために readonly
が組み込まれていることがよくあります。
type MyProps = {
readonly message: string;
};
type MyState = {
readonly count: number;
};
これは React.Component<P,S>
がすでに不変としてマークしているため必要ありません。(PR と議論をご覧ください!)
クラスメソッド:通常どおり実行しますが、関数に引数がある場合は、それらも型指定する必要があることを忘れないでください。
class App extends React.Component<{ message: string }, { count: number }> {
state = { count: 0 };
render() {
return (
<div onClick={() => this.increment(1)}>
{this.props.message} {this.state.count}
</div>
);
}
increment = (amt: number) => {
// like this
this.setState((state) => ({
count: state.count + amt,
}));
};
}
クラスプロパティ:後で使用するクラスプロパティを宣言する必要がある場合は、state
のように宣言しますが、代入はしません。
class App extends React.Component<{
message: string;
}> {
pointer: number; // like this
componentDidMount() {
this.pointer = 3;
}
render() {
return (
<div>
{this.props.message} and {this.pointer}
</div>
);
}
}
getDerivedStateFromProps の型指定
getDerivedStateFromProps
の使用を開始する前に、ドキュメンテーション とYou Probably Don't Need Derived State をご覧ください。派生ステートは、メモ化のセットアップにも役立つ可能性のあるフックを使用して実装できます。
以下に getDerivedStateFromProps
をアノテーションする方法をいくつか示します。
- 派生ステートを明示的に型指定し、
getDerivedStateFromProps
からの戻り値がそれに準拠していることを確認する場合。
class Comp extends React.Component<Props, State> {
static getDerivedStateFromProps(
props: Props,
state: State
): Partial<State> | null {
//
}
}
- 関数の戻り値をステートを決定させる場合。
class Comp extends React.Component<
Props,
ReturnType<typeof Comp["getDerivedStateFromProps"]>
> {
static getDerivedStateFromProps(props: Props) {}
}
- 他のステートフィールドやメモ化による派生ステートが必要な場合。
type CustomValue = any;
interface Props {
propA: CustomValue;
}
interface DefinedState {
otherStateField: string;
}
type State = DefinedState & ReturnType<typeof transformPropsToState>;
function transformPropsToState(props: Props) {
return {
savedPropA: props.propA, // save for memoization
derivedState: props.propA,
};
}
class Comp extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
otherStateField: "123",
...transformPropsToState(props),
};
}
static getDerivedStateFromProps(props: Props, state: State) {
if (isEqual(props.propA, state.savedPropA)) return null;
return transformPropsToState(props);
}
}