使用?TypeScript?開(kāi)發(fā)?React?函數(shù)式組件
前言
在我們使用 React 開(kāi)發(fā)項(xiàng)目時(shí),使用最多的應(yīng)該都是組件,組件又分為函數(shù)組件和類組件,我們可以這么定義:
定義函數(shù)組件:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
定義類組件:
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
包提供的類型,里面有一個(gè)通用類型 FC
,允許我們?yōu)楹瘮?shù)組件添加類型。
type FCProps = { text: string }; // React.FunctionComponent 的簡(jiǎn)寫 const FCComponent: React.FC<FCProps> = ({ text = "" }) => <div>{text}</div>;
這里的 React.FC
是 React.FunctionComponent
的簡(jiǎn)寫。
當(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
類型作為函數(shù)式組件的返回值類型,當(dāng)組件的返回值不是 JSX.Element
類型時(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. 直接定義完整類型
由于 React
組件包含子元素時(shí),會(huì)隱式傳遞一個(gè) children
屬性,導(dǎo)致定義的參數(shù)類型出錯(cuò),因此我們可以直接定義一個(gè)完整的參數(shù)接口,包含了 children
屬性的類型:
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)寫一個(gè) children
屬性類型比較麻煩,這時(shí)候我們就可以使用 React.PropsWithChildren
類型,它本身封裝了 children
的類型聲明:
// react/index.d.ts type PropsWithChildren<P> = P & { children?: ReactNode };
因此,使用 React.PropsWithChildren
類型定義函數(shù)式組件,就不用去處理 children
的類型了:
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)然你也不能這樣寫,當(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ù)的返回值類型:
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-07table 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-12JavaScript錯(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