讨论几个问题,react 组件的声明?react 高阶组件的声明和使用?class组件中 props 和 state 的使用?...javascript
jsx
语法的文件都须要以tsx
后缀命名Component<P, S>
泛型参数声明,来代替PropTypes!global.d.ts
中进行声明定义types/
目录下定义好其结构化类型声明react中的组件从定义方式上来讲,分为类组件和函数式组件。html
类组件的声明java
class App extends Component<IProps, IState> {
static defaultProps = {
// ...
}
readonly state = {
// ...
};
// 小技巧:若是state很复杂不想一个个都初始化,能够结合类型断言初始化state为空对象或者只包含少数必须的值的对象: readonly state = {} as IState;
}
复制代码
须要特别强调的是,若是用到了
state
,除了在声明组件时经过泛型参数传递其state
结构,还须要在初始化state
时声明为readonly
react
这是由于咱们使用 class properties
语法对state
作初始化时,会覆盖掉Component<P, S>
中对state
的readonly
标识。数组
// SFC: stateless function components
// v16.7起,因为hooks的加入,函数式组件也可使用state,因此这个命名不许确。新的react声明文件里,也定义了React.FC类型^_^
const List: React.SFC<IProps> = props => null
复制代码
是的
。只要在组件内部使用了props
和state
,就须要在声明组件时指明其类型。state
,貌似即便没有声明state的类型,也能够正常调用以及setState
。没错,实际状况确实是这样的,可是这样子作实际上是让组件丢失了对state
的访问和类型检查!// bad one
class App extends Component {
state = {
a: 1,
b: 2
}
componentDidMount() {
this.state.a // ok: 1
// 假如经过setState设置并不存在的c,TS没法检查到。
this.setState({
c: 3
});
this.setState(true); // ???
}
// ...
}
// React Component
class Component<P, S> {
constructor(props: Readonly<P>);
setState<K extends keyof S>(
state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
callback?: () => void
): void;
forceUpdate(callBack?: () => void): void;
render(): ReactNode;
readonly props: Readonly<{ children?: ReactNode }> & Readonly<P>;
state: Readonly<S>;
context: any;
refs: {
[key: string]: ReactInstance
};
}
// interface IState{
// a: number,
// b: number
// }
// good one
class App extends Component<{}, { a: number, b: number }> {
readonly state = {
a: 1,
b: 2
}
//readonly state = {} as IState,断言所有为一个值
componentDidMount() {
this.state.a // ok: 1
//正确的使用了 ts 泛型指示了 state 之后就会有正确的提示
// error: '{ c: number }' is not assignable to parameter of type '{ a: number, b: number }'
this.setState({
c: 3
});
}
// ...
}
复制代码
什么是 react 高阶组件?装饰器?react-router
props
进行修改(添加、删除)等。这些会致使签名一致性校验失败,TS
会给出错误提示。这带来两个问题:withRouter
:import { RouteComponentProps } from 'react-router-dom';
const App = withRouter(class extends Component<RouteComponentProps> {
// ...
});
// 如下调用是ok的
<App />
复制代码
如上的例子,咱们在声明组件时,注解了组件的props是路由的
RouteComponentProps
结构类型,可是咱们在调用App组件时,并不须要给其传递RouteComponentProps
里说具备的location
、history
等值,这是由于withRouter
这个函数自身对齐作了正确的类型声明。app
Partial
这个映射类型),或者将其声明到额外的injected
组件实例属性上。 咱们先看一个常见的组件声明:import { RouteComponentProps } from 'react-router-dom';
// 方法一
@withRouter
class App extends Component<Partial<RouteComponentProps>> {
public componentDidMount() {
// 这里就须要使用非空类型断言了
this.props.history!.push('/');
}
// ...
});
// 方法二
@withRouter
class App extends Component<{}> {
get injected() {
return this.props as RouteComponentProps
}
public componentDidMount() {
this.injected.history.push('/');
}
// ...
复制代码
interface IUserCardProps {
name: string;
avatar: string;
bio: string;
isAdmin?: boolean;
}
class UserCard extends Component<IUserCardProps> { /* ... */}
复制代码
上面的组件要求了三个必传属性参数:name、avatar、bio,isAdmin是可选的。加入此时咱们想要声明一个高阶组件,用来给UserCard传递一个额外的布尔值属性visible,咱们也须要在UserCard中使用这个值,那么咱们就须要在其props的类型里添加这个值:less
interface IUserCardProps {
name: string;
avatar: string;
bio: string;
visible: boolean;
isAdmin?: boolean;
}
@withVisible
class UserCard extends Component<IUserCardProps> {
render() {
// 由于咱们用到visible了,因此必须在IUserCardProps里声明出该属性
return <div className={this.props.visible ? '' : 'none'}>...</div>
}
}
function withVisiable(WrappedComponent) {
return class extends Component {
render() {
return <WrappedComponent {..this.props} visiable={true} /> } } } 复制代码
可能你此时想到了,把visible声明为可选。没错,这个确实就解决了调用组件时visible必传的问题。这确实是个解决问题的办法。可是就像上一个问题里提到的,这种应对办法应该是对付哪些没有类型声明或者声明不正确的高阶组件的。dom
因此这个就要求咱们能正确的声明高阶组件:函数
interface IVisible {
visible: boolean;
}
//排除 IVisible
function withVisible<Self>(WrappedComponent: React.ComponentType<Self & IVisible>): React.ComponentType<Omit<Self, 'visible'>> {
return class extends Component<Self> {
render() {
return <WrappedComponent {...this.props} visible={true} /> } } } 复制代码
如上,咱们声明withVisible这个高阶组件时,利用泛型和类型推导,咱们对高阶组件返回的新的组件以及接收的参数组件的props都作出类型声明。