介紹
在構(gòu)建React應用程序時,通常需要在組件之間共享某些組件邏輯罪裹。 React可以使用許多模式來實現(xiàn)這一目標宾肺,其中最先進的塑崖,也是最受歡迎的模式是高階組件文黎。 本指南將說明如何使用Typescript語言在React中使用高階組件來確保某些類型的安全性惹苗。
什么是高階組件?
高階組件類似于在函數(shù)編程中廣泛使用的高階函數(shù)模式耸峭。
盡可能簡單地說桩蓉,高階組件是一個將組件作為參數(shù)并返回新組件的函數(shù)。 該函數(shù)應該是一個純函數(shù)劳闹,因為它不會修改傳遞的組件并且沒有其他副作用院究,并且通常會將傳遞的組件包裝在另一個組件中以添加一些行為或注入一些預期的數(shù)據(jù),或者有時兩者玷或。
可以在此處找到更完整的React高階組件描述儡首。
React組件
本指南將從包含標題片任,正文和頁腳的React頁面開始偏友,該頁面將在掛載時進行API調(diào)用以檢索某些數(shù)據(jù)并將其顯示在正文中。 然后对供,這將用于創(chuàng)建兩個高階組件位他,一個將在頁眉和頁腳中顯示一個組件,另一個將從API讀取數(shù)據(jù)并將數(shù)據(jù)注入組件的props产场。
初始組件的代碼在這里:
class Page extends React.Component {
state = { things: [] as string[] };
async componentDidMount() {
const things = await getThings();
this.setState({ things });
}
render() {
return (
<>
<header className="app-header">
....
</header>
<div className="app-body">
<ul>
{this.state.things.map(thing => (
<li key={thing}>{thing}</li>
))}
</ul>
</div>
<footer className="app-footer">
...
</footer>
</>);
}
}
使用高階組件
首先要做的是創(chuàng)建一個新的高階組件鹅髓,它將一個組件顯示在頁面的正文部分,將該組件放在一個<div>中京景,并提供頁眉和頁腳元素窿冯。
此函數(shù)的簽名如下所示:
function withHeaderAndFooter<T>(Component: React.ComponentType<T>)
在這個簽名中,<T>是一個Typescript泛型類型确徙。在這種情況下醒串,T表示在渲染高階組件時傳遞的組件props的類型执桌,并且由于沒有注入新的props,返回的組件應該擁有與原版相同類型的props芜赌。 完整功能的代碼在這里:
function withHeaderAndFooter<T>(Component: React.ComponentType<T>) {
return (props: T) => (
<>
<header className="app-header">
...
</header>
<div className="app-body">
<Component {...props} />
</div>
<footer className="app-footer">
...
</footer>
</>);
}
此組件以與原react組件相同的方式呈現(xiàn)頁眉和頁腳仰挣,并在正文<div>中呈現(xiàn)傳遞的組件。傳遞到高階組件的props使用對象擴展運算符傳遞到此組件中缠沈,如此{ ...props}
膘壶。
在高階組件中注入props
將props注入組件可能是更高階組件的更常用的用例。
原始react組件調(diào)用API來獲取一些數(shù)據(jù)然后呈現(xiàn)它洲愤。這種行為也可以被提取到一個高階組件中颓芭,該組件將在掛載時調(diào)用API并通過其props將數(shù)據(jù)傳遞到提供的組件中。
首先要做的是定義一個將數(shù)據(jù)描述為prop的接口:
interface ThingsProps {
things: string[];
}
然后函數(shù)簽名將如下所示:
function withThings<T extends ThingsProps>(Component: React.ComponentType<T>)
在這種情況下柬赐,簽名使用泛型類型T指定props類型并擴展ThingsProps接口畜伐,這意味著傳遞給此函數(shù)的任何組件必須在其props中實現(xiàn)該接口。
因為things
prop是由高階組件注入的躺率,所以直接返回props的組件是不正確的玛界,因為組件的任何使用者都需要包含ThingsProps
, 解決此問題的一種方法是使用Omit
運算符悼吱,它接受一個類型T并從中刪除ThingsProps
類型中存在的任何屬性慎框。然后,這個高階組件可以返回一個類型為Omit <T后添,ThingsProps>
的props組件笨枯,使用者不需要提供ThingsProps
,從而避免了Typescript編譯器將拋出錯誤遇西。
該函數(shù)的代碼在這里:
function withThings<T extends ThingsProps>(Component: React.ComponentType<T>) {
return (props)<Omit<T, keyof ThingsProps>> => {
const [things, setThings] = React.useState([] as string[]);
const fetchThings = async () => {
const things = await getThings();
setThings(things);
}
React.useEffect(() => {
fetchThings()
}, []);
render() {
return <Component {...this.props as T} things={things} />;
}
};
}
渲染此組件時馅精,使用{ ... this.props }
將props
傳遞到高階組件,同時thing
這個屬性prop
也設置為things
的當前值粱檀,因此在使用這個組件時thing
這個屬性將使用API調(diào)用中的數(shù)據(jù)賦值洲敢。在這種情況下,必須將this.props
強制轉(zhuǎn)換為類型T茄蚯,否則压彭,Typescript編譯器將拋出錯誤。
使用高階的組件
正如預期的那樣渗常,使用這些新的高階組件就是一個用現(xiàn)有組件調(diào)用該函數(shù)的情況壮不,該組件具有正確的props
并呈現(xiàn)函數(shù)的結(jié)果。 所以withHeaderAndFoote
r高階組件可以像這樣使用:
function helloWorld() {
return <div>Hello world</div>;
}
const HelloWorldPage = withHeaderAndFooter(helloWorld);
return <HelloWorldPage />;
HelloWorldPage
組件現(xiàn)在由標題皱碘,正文和頁腳組成询一,正文中顯示文本“Hello world”。
將helloWorld
組件傳遞給withThings
高階組件將導致Typescript編譯器出錯,因為withThings
需要一個具有ThingsProps
定義thing
屬性的組件健蕊。這個高階組件可以像這樣使用:
function helloThings(props: ThingsProps) {
return (
<ul>
{props.things.map(thing => (
<li key={thing}>{thing}</li>
))}
</ul>);
}
const HelloThingsPage = withThings(helloThings);
return <HelloThingsPage />;
正如在創(chuàng)建withThings
組件時所討論的那樣缓醋,在渲染時不需要傳遞thing prop
,因為這是由高階組件處理的绊诲。
創(chuàng)建一個由withHeaderAndFooter
和withThings
高階組件組成的組件只是將結(jié)果從一個傳遞到另一個的問題送粱。因此,創(chuàng)建一個組件掂之,用于將頁眉抗俄,頁腳和正文中的helloThings
組件包裝在頁面中還注入了thing
,可以這樣做:
const HelloThingsPage = withThings(helloThings);
const FullPage = withHeaderAndFooter(HelloThingsPage);
或者像這樣組成一行:
const FullPage = withHeaderAndFooter(withThings(helloThings));
使用<FullPage />
生成的FullPage
組件現(xiàn)在與原始組件相同世舰。
調(diào)用高階組件的順序沒有區(qū)別动雹,withThings(withHeaderAndFooter(helloThings))
可以實現(xiàn)相同的結(jié)果。
參考
ts-higher-order-components
Higher Order Composition with Typescript for React
TypeScript 高級技巧