使用Typescript在react中編寫高階組件

介紹

在構(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)該接口。

因為thingsprop是由高階組件注入的躺率,所以直接返回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é)果。 所以withHeaderAndFooter高階組件可以像這樣使用:

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)建一個由withHeaderAndFooterwithThings高階組件組成的組件只是將結(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 高級技巧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末跟压,一起剝皮案震驚了整個濱河市胰蝠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌震蒋,老刑警劉巖茸塞,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異查剖,居然都是意外死亡钾虐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門笋庄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來效扫,“玉大人,你說我怎么就攤上這事直砂【剩” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵静暂,是天一觀的道長济丘。 經(jīng)常有香客問我,道長籍嘹,這世上最難降的妖魔是什么闪盔? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮辱士,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘听绳。我一直安慰自己颂碘,他們只是感情好,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著头岔,像睡著了一般塔拳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上峡竣,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天靠抑,我揣著相機與錄音,去河邊找鬼适掰。 笑死,一個胖子當著我的面吹牛类浪,可吹牛的內(nèi)容都是我干的载城。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼费就,長吁一口氣:“原來是場噩夢啊……” “哼诉瓦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起力细,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤睬澡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后眠蚂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體猴贰,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年河狐,在試婚紗的時候發(fā)現(xiàn)自己被綠了米绕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡馋艺,死狀恐怖栅干,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捐祠,我是刑警寧澤碱鳞,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站踱蛀,受9級特大地震影響窿给,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜率拒,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一崩泡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧猬膨,春花似錦角撞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽热康。三九已至,卻和暖如春劣领,著一層夾襖步出監(jiān)牢的瞬間姐军,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工尖淘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留奕锌,地道東北人。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓德澈,卻偏偏與公主長得像歇攻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子梆造,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容