「ランサーズ Advent Calendar 2018」9日目の記事になります。
ランサーズでは最近Reactのフロントエンドの開発にTypeScriptを導入しました。
導入当初の自分のスペックは以下の通りでした。
- React/Reduxで過去にプロダクト開発経験アリ
- Flowはすこし使ったことがある
- TypeScriptはまったく触ったことがない
過去にFlowを使ったことがあったのでTypeScriptに乗り換えても何とかなるだろうと思っていたのですが、
ReduxとReactのconnectでベストプラクティスが見つからずに苦労したのでまとめます。
TL;DR
import * as React from 'react';
import { bindActionCreators, Dispatch } from 'redux';
import { connect } from 'react-redux';
class UserComponent extends React.Component<Props> {
render() {
const { user, title } = this.props;
return (
<div>
<div>{title}</div>
<div>{user.name}</div>
</div>
);
}
}
interface User {
name: string;
}
interface ownProps {
title: string;
}
interface StateProps {
user: User;
}
interface DispatchProps {
actions: {
getUserStart: Function;
};
}
function mapStateToProps(state: { user: User }): StateProps {
return {
user: state.user,
};
}
function mapDispatchToProps(dispatch: Dispatch): DispatchProps {
return {
actions: bindActionCreators({ getUserStart }, dispatch),
};
}
type Props = ownProps & StateProps & DispatchProps;
export default connect<StateProps, DispatchProps>(
mapStateToProps,
mapDispatchToProps
)(UserComponent);
解説
コンポーネントにpropsの型を渡す
class UserComponent extends React.Component<Props> {
コンポーネントを定義するときはReact.Component
のジェネリクス<>
にpropsの型Props
を渡します。
ここで渡す型はattributesとして渡すpropsやreduxから渡すpropsなどすべてを含めたものになります。
Propsを定義する
次にownProps
,StateProps
,DispatchProps
の3つの型をつくります。
それぞれの型は以下のpropsに対応しています。
- ownProps → attributesとして渡すprops
- StateProps → mapStateToPropsで注入するprops
- DispatchProps → mapDispatchToPropsで注入するprops
これらの型を&
でマージします。
マージしてできた型Props
は前節で説明したようにReact.Component
のジェネリクスに渡します。
type Props = ownProps & StateProps & DispatchProps;
connectする
最後にreduxとコンポーネントをconnectします。
connectのジェネリクスにStateProps
とDispatchProps
を渡します。
export default connect<StateProps, DispatchProps>(
mapStateToProps,
mapDispatchToProps
)(UserComponent);
作成したコンポーネントは下のように使うことができます。
ownProps
で定義したpropsはattributesとして値を渡すことができます。
import * as React from 'react';
import User from './UserComponent';
const App = () => (
<div>
<User title={'title'} />
</div>
)
まとめ
TypeScriptを導入しはじめのときは、どうやって型を定義してジェネリクスに渡せばよいのかわからずにはまってしまうことが多かったです。
特にTypeScriptでライブラリを使うときはドキュメントもなく、自分で型定義ファイルを読み進めて実装する必要があるので最初は大変です。
しかし、型に慣れてきた今ではTypeScriptの恩恵を存分に受けており、開発スピードも向上していると感じています。
JavaScriptで開発をされている方は一度トライしてみるとよいと思います。
追記
最近更に簡潔に型定義する方法を見つけました。
ReturnTypeとtypeofを組み合わせることでmapStateToPropsとmapDispatchToPropsの型定義をせずにconnectできます。最高。
import * as React from 'react';
import { bindActionCreators, Dispatch } from 'redux';
import { connect } from 'react-redux';
class UserComponent extends React.Component<Props> {
render() {
const { user, title } = this.props;
return (
<div>
<div>{title}</div>
<div>{user.name}</div>
</div>
);
}
}
interface User {
name: string;
}
interface ownProps {
title: string;
}
function mapStateToProps(state: { user: User }) {
return {
user: state.user,
};
}
function mapDispatchToProps(dispatch: Dispatch) {
return {
actions: bindActionCreators({ getUserStart }, dispatch),
};
}
type Props = ownProps & ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>;
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserComponent);