この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、CX事業本部 IoT事業部の若槻です。
前回のエントリではReact Routerでprops.history
を使用したルーティングを実装しました。
今回は、このReact RouterのpropsをTypeScriptで型付けしてみました。
やってみた
react-router-domのRouteComponentProps
を使用すれば、propsを型付け可能です。
propsによる値の受け渡しがない場合
まず、コンポーネント間でpropsによる値の受け渡しがない場合。
RouteComponentProps
をReact.FC
にジェネリックで渡すようにすればOKです。
src/Home.tsx
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
const Home: React.FC<RouteComponentProps> = (props) => {
return (
<div>
<h1>Home page</h1>
<ul>
<li>
<button
onClick={() => {
props.history.push({
pathname: '/profile',
state: { sourcePage: 'Home' }
});
}}
>
Go to my profile
</button>
</li>
</ul>
</div>
);
};
export default Home;
ソースコードを見ると、React.FC
のジェネリックに指定した型は引数props
の型としてそのまま使われているためです。
type FC<P = {}> = FunctionComponent<P>;
interface FunctionComponent<P = {}> {
(props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
propTypes?: WeakValidationMap<P> | undefined;
contextTypes?: ValidationMap<any> | undefined;
defaultProps?: Partial<P> | undefined;
displayName?: string | undefined;
}
引数に直接RouteComponentProps
を型として指定することもできますが、基本的にReact.FC
を使えば良いと思います。
src/Home.tsx
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
const Home = (props: RouteComponentProps) => {
return (
<div>
中略
</div>
);
};
export default Home;
propsによる値の受け渡しがある場合
propsによる値の受け渡しがある場合だと、同じようにRouteComponentProps
を直接React.FC
のジェネリックで使用するとエラーとなります。
src/MyProfile.tsx(エラー)
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
const MyProfile: React.FC<RouteComponentProps> = (
props
) => {
return (
<div>
<h1>This is my profile</h1>
<ul>
{props.location.state && (
<li>
<button
onClick={() => {
props.history.goBack();
}}
>
Back {props.location.state.sourcePage}
</button>
</li>
)}
</ul>
</div>
);
};
export default MyProfile;
props.location.state
にsourcePage
が無いためエラーとなっています。
ここでRouteComponentProps
のソースコードを見ると、これまたジェネリックでS
、つまり第三引数を指定すれば良さげですね。
export interface RouteComponentProps<
Params extends { [K in keyof Params]?: string } = {},
C extends StaticContext = StaticContext,
S = H.LocationState
> {
history: H.History<S>;
location: H.Location<S>;
match: match<Params>;
staticContext?: C | undefined;
}
というわけで次のようにしたらエラー無く型付けできるようになります。
src/MyProfile.tsx
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
type Props = RouteComponentProps<
{},
{},
{ sourcePage: string }
>;
const MyProfile: React.FC<Props> = (props) => {
return (
<div>
<h1>This is my profile</h1>
<ul>
{props.location.state && (
<li>
<button
onClick={() => {
props.history.goBack();
}}
>
Back {props.location.state.sourcePage}
</button>
</li>
)}
</ul>
</div>
);
};
export default MyProfile;
デモ
ここまでのコードを使用したデモです。
参考
- TypeScriptの目玉機能「ジェネリック(Generics)」はこうなっている - Build Insider
- React Router with TypeScriptでちゃんと型をつける - Qiita
以上