環境搭建
我們當然可以先用腳手架搭建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>
);
}
}
