React 的 prop type 定義一直以來都很重要,可以減少很多 debug 要花的時間,而對於最常用的 render props 的定義通常就會用 PropTypes.func
來作為 prop type 的定義
const Select = ({ renderOption, options, ...rest }) => {
return <select {...rest}>{renderOption(options)}</select>;
};
Select.propTypes = {
options: PropTypes.array.isRequired,
renderOption: PropTypes.func.isRequired,
};
如果是用 children 的話,通常會使用 PropTypes.node
(可以是任意個) 或是 PropTypes.element
(限定一個 child) 來表示任何可以被 render 出來的東西
const Select = ({ children, ...rest }) => {
return <select {...rest}>{children}</select>;
};
Select.propTypes = {
children: PropTypes.node,
};
又如果限定 Component 來 render 呢?那樣可能就要用 PropTypes.instanceOf
(如果有多種 instances 可能的話,就要在搭配 PropTypes.oneOfType
)
const Select = ({ children, ...rest }) => {
return <select {...rest}>{children}</select>;
};
Select.propTypes = {
children: PropTypes.oneOfType([
PropTypes.instanceOf(Option),
PropTypes.instanceOf(OptionGroup),
]),
};
最後如果要傳額外的 prop 給 children,並且由 children 自己 render 的話就會像是以下這樣
const Select = ({ children, options, ...rest }) => {
return <select {...rest}>{options.map((opt) => children(opt))}</select>;
};
Select.propTypes = {
options: PropTypes.array.isRequired,
children: PropTypes.func.isRequired,
};
const Option = ({ name, value }) => {
return <option value={value}>{name}</option>;
};
Option.propTypes = {
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
};
const App = () => {
return <Select>{Option}</Select>;
};
問題來了,如果要避免 re-render 可能需要上 React.memo
或是用 PureComponent
來優化 Option
,而出來的 Option 將不會是一個 function 會是一個 React element 的 struct,所以 propTypes 的定義就會變成
Select.propTypes = {
children: PropTypes.oneOf([
PropTypes.func,
PropTypes.shape({
$$typeof: PropTypes.symbol,
}),
]),
};
而這很煩人要寫很多,所以在 prop-types^15.7.0
就提供了新的寫法
Select.propTypes = {
children: PropTypes.elementType,
};
最後總結一下
PropTypes.func; // () => {}
PropTypes.element; // <Component />
PropTypes.node; // <Component /> * 0 ~ N
PropTypes.elementType; // Component