React TypeScript Cheatsheet を見ていたら、React.FCを非推奨してたので、理由を少し調べてみた。
主な理由はこのCRAのprに書いてあった。
https://github.com/facebook/create-react-app/pull/8177
children
の定義してしまうconst App: React.FC = () => { /*... */ };
const Example = () => {
// <App/> はchildrenを利用しないが、渡せてしまう。
<App><div>Unwanted children</div></App>
}
type GenericComponentProps<T> = {
prop: T
callback: (t: T) => void
}
const GenericComponent = <T>(props: GenericComponentProps<T>) => {/*...*/}
しかし、React.FCを使用する場合genericの T
を保持、返却する仕組みがないため、これらが不可能です。
const GenericComponent: React.FC</* ??? */> =
<T>(props: GenericComponentProps<T>) => {/*...*/}
children
)のは、やや一般的なパターンです。<Select>
<Select.Item />
</Select>
これはReact.FCでも可能ですが、以下のように厄介なことになります。
// 依存するコンポーネントの型を定義しないといけない
const Select: React.FC<SelectProps> & { Item: React.FC<ItemProps> } =
(props) => {/* ... */ }
Select.Item = (props) => { /*...*/ }
React.FCがないと問題なく動作します
const Select = (props: SelectProps) => {/* ... */}
Select.Item = (props: ItemProps) => {/* ...*/ }。
type ComponentProps = { name: string; }
const Component = ({ name }: ComponentProps) => (<div>
{name.toUpperCase()} /* Safe since name is required */
</div>);
Component.defaultProps = { name: "John" };
const Example = () => (<Component />) /* Safe to omit since name has a default value */
これは正しくコンパイルされる。
React.FCを利用した場合は、少しだけ間違ったものになる
React.FC<{name: string}>
を利用した場合、nameはオプションではなく必須となりReact.FC<{name?: string}>
とした場合はtoUpperCase()でエラーになってしまうなので「内部的には必須、外部的にはオプション」という動作を再現する方法はないです。
※ defaultPropsは非推奨となるのでそもそも気にしなくてもよい(デフォルト値はESのを利用する。)
React.FC
を利用しているよりは短く書けるconst C1: React.FC<CProps> = (props) => {};
const C2 = (props: CProps) => {};
React.FC
の場合は戻り値を明示的に定義できる点であり、以下のようにundefindを返す場合は、コンパイル時にエラーとならない。const Component = () => {
// コンポーネントはundefinedを返すことはできない(`null`のみ)
return undefined;
}
ただし、この場合、実際には実行時にエラーとして補足されるので、あまり形式的には変わらないかも?
// コンポーネントの戻り値が正しくないのでエラーになる
const Example = () => <Component />;
JSX.Element
を戻り値の型としてしたほうがよい(個人的には)const App = ({ message }: AppProps): JSX.Element => <div>{message}</div>;