環境搭建
我們當然可以先用腳手架搭建React項目,然后手動配置成支持TypeScript的環境,雖然比較麻煩,但可以讓你更清楚整個過程。這里比較麻煩,就不演示了,直接用命令配置好。
npx create-react-app appname --typescript
可以安裝一些自己需要的庫及其聲明文件,例如react-router-dom、axios、ant Design等。如果要安裝ant design,還需要在開發環境庫中安裝一些依賴庫,以幫助實現按需加載。
使用
有類型約束的函數組件
import React from "react"; import { Button } from "antd"; interface Greeting { name: string; firstName?: string; lastName?: string; } // 沒有使用React.FC const HelloOld = (props: Greeting) => <Button>你好{props.name}</Button>; // 使用React.FC泛型類型 const Hello: React.FC<Greeting> = (props) => { return ( <Button>Hello {props.name}</Button> ) }; export { Hello, HelloOld };
定義函數組件時,使用React.FC與不使用沒有太多區別,沒有為我們帶來明顯的好處,建議使用常規定義方式。關於使用與不使用的區別,詳見 https://github.com/facebook/create-react-app/pull/8177
有類型約束的類組件
import React,{Fragment} from "react"; import { Button } from "antd"; interface Greeting { name: string; firstName?: string; lastName?: string; } interface State { count: number } // 泛型類型,第一個傳入參數約束屬性props,第二個約束狀態state(內部數據) class HelloClass extends React.Component<Greeting, State> { state: State = { count: 0 }; static defaultProps = { // 屬性默認值 firstName: "", lastName: "", }; render() { return ( <Fragment> <p>點擊了{this.state.count}次</p> <Button onClick={()=>{this.setState({count: this.state.count+1})}}>Hello{this.props.name}Class</Button> </Fragment> ); } } export default HelloClass;
有類型約束的高階組件
import React from "react"; import HelloClass from "./HelloClass"; interface Loading { loading: boolean; } function HelloHoc<P>(params?: any) { return function<P>(WrappedComponent: React.ComponentType<P>) { // P表示被包裝組件的屬性的類型 return class NewComponent extends React.Component<P & Loading>{ // 這里使用交叉類型,為新組件增加一些屬性,接口Loading定義了新增的屬性聲明 render(){ return this.props.loading ? <div>Loading</div> : <WrappedComponent {...this.props as P}/> } } } } export default HelloHoc()(HelloClass);
高階組件在ts中使用會有比較多的類型問題,解決這些問題通常不會很順利,而且會存在一些已知的bug,這不是高階組件本身的問題,而是React聲明文件還沒有很好地兼容高階組件的類型檢查,更好的方式是使用Hooks
有類型約束的Hooks
import React, { useState, useEffect } from "react"; import { Button } from "antd"; interface Greeting { name: string; firstName?: string; lastName?: string; } const HelloHooks = (props: Greeting) => { const [ count, setCount ] = useState(0); // 設了初值,所以不用定義類型 const [ text, setText ] = useState<string | null>(null); useEffect(()=>{ count > 5 && setText("休息一下"); },[count]); // 第二個參數的作用是,只有當count改變的時候,函數內的邏輯才會執行。 return ( <> <p>你點擊了Hooks {count} 次 {text}</p> <Button onClick={()=>{setCount(count+1)}}>{props.name}</Button> </> ); }; export default HelloHooks;
事件綁定
class HelloClass extends React.Component<Greeting, State> { state: State = { count: 0 }; clickHandle = (e: React.MouseEvent) => { // 事件對象e的類型使用內置的合成事件。在回調函數中,e的屬性都會無效 e.persist(); // 將該事件從池中刪除合成事件,可以正常使用 console.log(e); // this.setState({count: this.state.count+1}) }; inputHandle = (e: React.FormEvent<HTMLInputElement>) => { // e.persist(); console.log(e.currentTarget.value); // 此時編譯器報錯,認為沒有value屬性,需要指定<HTMLInputElement>泛型類型 // console.log(e.target.value); // 仍然不行 }; render() { return ( <Fragment> <p>點擊了{this.state.count}次</p> <Button onClick={this.clickHandle}>Hello{this.props.name}Class</Button> <input onChange={this.inputHandle}/> </Fragment> ); } }