使用?TypeScript?開(kāi)發(fā)?React?函數(shù)式組件
前言
在我們使用 React 開(kāi)發(fā)項(xiàng)目時(shí),使用最多的應(yīng)該都是組件,組件又分為函數(shù)組件和類(lèi)組件,我們可以這么定義:
定義函數(shù)組件:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}定義類(lèi)組件:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}這篇文章我會(huì)和大家介紹使用 TypeScript 定義函數(shù)式組件的 4 種方法,還有幾個(gè)使用過(guò)程中需要注意的問(wèn)題。
如何使用 TypeScript 定義函數(shù)式組件
函數(shù)式組件通常接受一個(gè) props 參數(shù),返回一個(gè) JSX 元素或者 null。
當(dāng)我們需要使用 TypeScript 去定義一個(gè)函數(shù)式組件時(shí),我們有 4 種方式,4 種方式各有各的優(yōu)缺點(diǎn),看具體情況使用。
1. 使用 React.FC
由于 React 不是使用 TypeScript 開(kāi)發(fā)的,使用的是社區(qū)開(kāi)發(fā)的 @type/react 包提供的類(lèi)型,里面有一個(gè)通用類(lèi)型 FC ,允許我們?yōu)楹瘮?shù)組件添加類(lèi)型。
type FCProps = { text: string };
// React.FunctionComponent 的簡(jiǎn)寫(xiě)
const FCComponent: React.FC<FCProps> = ({ text = "" }) => <div>{text}</div>;這里的 React.FC 是 React.FunctionComponent 的簡(jiǎn)寫(xiě)。
當(dāng)組件包含子元素,TypeScript 會(huì)提示警告:
type FCProps = { text: string };
const FCComponent: React.FC<FCProps> = ({ text = "" }) => <div>{text}</div>;
function App() {
return (
<div className="App">
<FCComponent text="Hello Chris1993.">
<span>children</span>
</FCComponent>
</div>
);
}提示警告內(nèi)容:
Type '{ children: string; text: string; }' is not assignable to type 'IntrinsicAttributes & FCProps'.
Property 'children' does not exist on type 'IntrinsicAttributes & FCProps'.現(xiàn)在不推薦使用這個(gè)了,具體討論可以看這兩個(gè)鏈接:
2. 使用 JSX.Element
使用 JSX.Element 類(lèi)型作為函數(shù)式組件的返回值類(lèi)型,當(dāng)組件的返回值不是 JSX.Element 類(lèi)型時(shí),TypeScript 就會(huì)提示錯(cuò)誤。
type FCProps = { text: string };
const ElementComponent = ({ text }: FCProps): JSX.Element => <div>{text}</div>;
function App() {
return (
<div className="App">
<ElementComponent text="Hello Chris1993."></ElementComponent>
</div>
);
}3. 直接定義完整類(lèi)型
由于 React 組件包含子元素時(shí),會(huì)隱式傳遞一個(gè) children 屬性,導(dǎo)致定義的參數(shù)類(lèi)型出錯(cuò),因此我們可以直接定義一個(gè)完整的參數(shù)接口,包含了 children 屬性的類(lèi)型:
type FCProps = { text: string; children?: any };
const FCComponent: React.FC<FCProps> = ({ text = "" }) => <div>{text}</div>;
function App() {
return (
<div className="App">
<FCComponent text="Hello Chris1993.">
<span>children</span>
</FCComponent>
</div>
);
}4. 使用 React.PropsWithChildren
第 3 種方法每次都要手動(dòng)寫(xiě)一個(gè) children 屬性類(lèi)型比較麻煩,這時(shí)候我們就可以使用 React.PropsWithChildren 類(lèi)型,它本身封裝了 children 的類(lèi)型聲明:
// react/index.d.ts
type PropsWithChildren<P> = P & { children?: ReactNode };因此,使用 React.PropsWithChildren 類(lèi)型定義函數(shù)式組件,就不用去處理 children 的類(lèi)型了:
type IProps = React.PropsWithChildren<{ text: string }>;
const PropsComponent = ({ text }: IProps) => <div>{text}</div>;
function App() {
return (
<div className="App">
<PropsComponent text="Hello Chris1993.">
<span>children</span>
</PropsComponent>
</div>
);
}使用過(guò)程需要注意的點(diǎn)
1. 函數(shù)式組件返回值不能是布爾值
當(dāng)我們?cè)诤瘮?shù)式組件內(nèi)使用條件語(yǔ)句時(shí),如果返回的是非 JSX 元素或者非 null 的值,React 將會(huì)報(bào)錯(cuò):
const ConditionComponent = ({ useRender = false }) =>
useRender ? <span>Render ConditionComponent</span> : false;// ?
function App() {
return (
<div className="App">
<ConditionComponent useRender></ConditionComponent>
{/* 'ConditionComponent' cannot be used as a JSX component.
Its return type 'false | Element' is not a valid JSX element.
Type 'boolean' is not assignable to type 'ReactElement<any, any>'.
*/}
</div>
);
}正確的處理方式,應(yīng)該是讓函數(shù)式組件返回一個(gè)有效的 JSX 元素或者 null:
const ConditionComponent = ({ useRender = false }) =>
useRender ? <span>Render ConditionComponent</span> : <span>error</span>;// ?
// or
const ConditionComponent = ({ useRender = false }) =>
useRender ? <span>Render ConditionComponent</span> : null;// ?當(dāng)然你也不能這樣寫(xiě),當(dāng)屬性 useRender 為 true 時(shí),也會(huì)出錯(cuò):
const ConditionComponent = ({ useRender = false }) =>
useRender && <span>Render ConditionComponent</span>;// ?2. 無(wú)法為組件使用 Array.fill() 填充
當(dāng)我們的組件直接返回 Array.fill() 的結(jié)果時(shí),TypeScript 會(huì)提示錯(cuò)誤。
const ArrayComponent = () => Array(3).fill(<span>Chris1993</span>); // ?
function App() {
return (
<div className="App">
<ArrayComponent></ArrayComponent>
</div>
);
}提示下面內(nèi)容:
'ArrayComponent' cannot be used as a JSX component.
Its return type 'any[]' is not a valid JSX element.
Type 'any[]' is missing the following properties from type 'ReactElement<any, any>': type, props, key為了解決這個(gè)問(wèn)題,我們可以定義函數(shù)的返回值類(lèi)型:
const ArrayComponent = () => Array(3).fill(<span>Chris1993</span>) as any as JSX.Element; // ?
3. 支持使用泛型來(lái)創(chuàng)建組件
在使用 TypeScript 開(kāi)發(fā) React 函數(shù)式組件的時(shí)候,也可以使用泛型進(jìn)行約束,聲明一個(gè)泛型組件(Generic Components),這樣可以讓我們的組件更加靈活。
可以這樣使用:
interface GenericProps<T> {
content: T;
}
const GenericComponent = <T extends unknown>(props: GenericProps<T>) => {
const { content } = props;
const component = <>{content}</>;
return <div>{component}</div>;
};
function App() {
return (
<div className="App">
{ /* Success ? */}
<GenericComponent<number> content={10} />
{ /* Error ? Type 'string' is not assignable to type 'number'. */}
<GenericComponent<number> content={"10"} />
</div>
);
}在 Generic Components 章節(jié)中介紹到更高級(jí)的使用方式:
interface Props<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
const List = <T extends unknown>(props: Props<T>) => {
const { items, renderItem } = props;
const [state, setState] = React.useState<T[]>([]); // You can use type T in List function scope.
return (
<div>
{items.map(renderItem)}
<button onClick={() => setState(items)}>Clone</button>
{JSON.stringify(state, null, 2)}
</div>
);
};
function App() {
return (
<div className="App">
<List<number>
items={[1, 2]} // type of 'string' inferred
renderItem={(item) => (
<li key={item}>
{/* Error: Property 'toPrecision' does not exist on type 'string'. */}
{item.toPrecision(3)}
</li>
)}
/>
</div>
);
}到此這篇關(guān)于使用 TypeScript 開(kāi)發(fā) React 函數(shù)式組件的文章就介紹到這了,更多相關(guān)TypeScript 開(kāi)發(fā) React內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
js+SVG實(shí)現(xiàn)動(dòng)態(tài)時(shí)鐘效果
這篇文章主要為大家詳細(xì)介紹了js+SVG實(shí)現(xiàn)動(dòng)態(tài)時(shí)鐘效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
table insertRow、deleteRow定義和用法總結(jié)
這篇文章主要對(duì)table insertRow、deleteRow定義和用法做下總結(jié),需要的朋友可以參考下2014-05-05
原生JS實(shí)現(xiàn)列表子元素順序反轉(zhuǎn)的方法分析
這篇文章主要介紹了原生JS實(shí)現(xiàn)列表子元素順序反轉(zhuǎn)的方法,結(jié)合實(shí)例形式分析了javascript針對(duì)dom元素、數(shù)組reverse方法、innerHTML方法等列表元素順序翻轉(zhuǎn)相關(guān)操作技巧,需要的朋友可以參考下2018-07-07
前端接口報(bào)錯(cuò)Required?request?body?is?missing解決辦法
這篇文章主要給大家介紹了關(guān)于前端接口報(bào)錯(cuò)Required?request?body?is?missing的解決辦法,文中通過(guò)代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-12-12
JavaScript錯(cuò)誤處理try..catch...finally+涵蓋throw+TypeError+RangeEr
這篇文章主要介紹了JavaScript錯(cuò)誤處理:try..catch...finally+涵蓋throw+TypeError+RangeError,文章內(nèi)容具有一定的參考價(jià)值,需要的小伙伴可以參考一下,希望對(duì)你有所幫助2021-12-12
在JavaScript里防止事件函數(shù)高頻觸發(fā)和高頻調(diào)用的方法
這篇文章主要介紹了在JavaScript里防止事件函數(shù)高頻觸發(fā)和高頻調(diào)用的方法,本文方法從Underscore.js中摘錄而來(lái),需要的朋友可以參考下2014-09-09

