Reactコンポーネントを作っている際、storybookからpropsなしで呼び出してもエラーにならない方法が欲しかったので調べました。
Optional Props
typeに?
をつければoptional propsを作れます。
import React from 'react';
export type HogeProps = {
name?: string;
age?: number;
};
const Hoge: React.FC<HogeProps> = (props: HogeProps) => {
return (
<>
<div>{props.name}</div>
<div>{props.age}</div>
</>
);
};
export default Hoge;
が、コンポーネント内で値をいじろうとするとundefined
の可能性があるためエラーとなります。
undefined
チェックを行えば回避可能ですがめんどくさいです。
import React from 'react';
export type HogeProps = {
name?: string;
age?: number;
};
const Hoge: React.FC<HogeProps> = (props: HogeProps) => {
const fuga = props.name.substring(0, 2); // Object is possibly 'undefined'.
const piyo = 100 + props.age; // Object is possibly 'undefined'.
const fugaValue = props.name === undefined ? 'undefined' : props.name // OK
return (
<>
<div>{fuga}</div>
<div>{piyo}</div>
</>
);
};
export default Hoge;
デフォルト値を設定する
defaultProps
でデフォルト値を設定できますが、関数内では(string or number) | undefined
なのでエラーは解決できません。
import React from 'react';
... // Object is possibly 'undefined'.
Hoge.defaultProps = {
name: "hage",
age: 35
}
export default Hoge;
解決法
これを解決するには、コンポーネントの呼び出しではundefined
を許容しつつ内部ではundefined
が除外された別のtype
として処理するようにします。
import React from 'react';
type PropsBase = {
name?: string;
age?: number;
};
const defaultValue = {
name: "hage",
age: 35
};
const PropsDefault: Required<
Pick<PropsBase, { [Key in keyof PropsBase]-?: Key }[keyof PropsBase]>
> = defaultValue;
type Props = PropsBase & typeof PropsDefault;
export { defaultValue as hogeDefaultValue };
export type HogeProps = Props;
const Hoge: React.FC<PropsBase> = (_props: PropsBase) => {
const props = (defaultValue && _props) as Props;
const fuga = props.name.substring(0, 2); // OK
const piyo = 100 + props.age; // OK
return (
<>
<div>{fuga}</div>
<div>{piyo}</div>
</>
);
};
Hoge.defaultValue = defaultValue;
export default Hoge;
----------
import React from 'react';
import Hoge from './Hoge'
const FooBar: React.FC = () => {
return(
<>
<Hoge> // OK
<Hoge name={"fusa"}> // OK
<Hoge name={"fusa"} age={50}> // OK
</>
)
};
export default FooBar;
テストの際などにどうなるのか調べてないのでコメントで教えていただければ嬉しいです。
参考
Why don't use optional? in props params when interface work with defaultProps ?
https://tinyurl.com/y4c33as9 (Credit: BY/bocong#4119 at Reactiflux Discord server)