react+ts的类型定义
React.FunctionComponent <Props>
定义函数组件
- 简写:
React.FC<Props>
const MyComponent:React.FC<Props> = ...
React.Component <Props, State>
定义class组件
- 简写:没有
const MyComponent:React.Component<Props, State> = ...
React.ComponentType <Props>
- 表示函数组件或class组件,用于高阶组件
const withState = <P extends WrappedComponentProps>(
WrappedComponent: React.ComponentType<P>,
) => { ...
React.ReactElement | JSX.Element
- react元素:可以是原始元素,或自定义组件
const elementOnly: React.ReactElement = <div /> || <MyComponent />;
React.ReactNode
- 一些React node类型:基本的react元素以及js的基本数据类型
const elementOrPrimitive: React.ReactNode = 'string' || 0 || false || null || undefined || <div /> || <MyComponent />;
const Component = ({ children: React.ReactNode }) => ...
React.ComponentProps<typeof XXX>
- 获取指定组件的props类型(注意:不适用于静态声明的默认props或者泛型props)
type MyComponentProps = React.ComponentProps<typeof MyComponent>;
React.CSSProperties
- 表示在JSX中使用的style对象
const styles: React.CSSProperties = { flexDirection: 'row', ...
const element = <div style={styles} ...
React.HTMLProps<HTMLXXXElement>
- 表示指定的HTML元素的props,用于扩展HTML元素
const Input: React.FC<Props & React.HTMLProps<HTMLInputElement>> = props => { ... }
<Input about={...} accept={...} alt={...} ... />
React.ReactEventHandler<HTMLXXXElement>
- 表示泛型事件,用于声明事件
const handleChange: React.ReactEventHandler<HTMLInputElement> = (ev) => { ... }
<input onChange={handleChange} ... />
React.XXXEvent<HTMLXXXElement>
- 表示更具体的事件
- 一些常见的例子:
ChangeEvent
,FormEvent
,FocusEvent
,KeyboardEvent
,MouseEvent
,DragEvent
,PointerEvent
,WheelEvent
,TouchEvent
const handleChange = (ev: React.MouseEvent<HTMLDivElement>) => { ... }
<div onMouseMove={handleChange} ... />
上面的代码例子,React.MouseEvent<HTMLDivElement>
是鼠标事件类型,这个事件是会发生在HTMLDivElement
上的。
使用例子
Function Components - FC
例子--计算器组件
// 定义
import * as React from 'react';
type Props = {
label: string;
count: number;
onIncrement: () => void;
};
export const FCCounter: React.FC<Props> = props => {
const { label, count, onIncrement } = props;
const handleIncrement = () => {
onIncrement();
};
return (
<div>
<span>
{label}: {count}
</span>
<button type="button" onClick={handleIncrement}>
{`Increment`}
</button>
</div>
);
};
// 使用
import * as React from 'react';
import { SFCCounter } from '@src/components';
export default class extends React.Component<{}, { count: number }> {
state = { count: 0 };
render() {
return (
<SFCCounter
label={'SFCCounter'}
count={this.state.count}
onIncrement={() => { this.setState({ count: this.state.count + 1 }); }}
/>
);
}
}
- 可以使用扩展属性
import * as React from 'react';
type Props = {
className?: string;
style?: React.CSSProperties;
};
export const FCSpreadAttributes: React.FC<Props> = props => {
const { children, ...restProps } = props;
return <div {...restProps}>{children}</div>;
};
Class Components
例子--计算器组件
// 定义
import * as React from 'react';
type Props = {
label: string;
};
type State = {
count: number;
};
export class ClassCounter extends React.Component<Props, State> {
readonly state: State = {
count: 0,
};
handleIncrement = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
const { handleIncrement } = this;
const { label } = this.props;
const { count } = this.state;
return (
<div>
<span>
{label}: {count}
</span>
<button type="button" onClick={handleIncrement}>
{`Increment`}
</button>
</div>
);
}
}
// 使用与函数组件一致
import * as React from 'react';
import { SFCCounter } from '@src/components';
export default class extends React.Component<{}, { count: number }> {
state = { count: 0 };
render() {
return (
<SFCCounter
label={'SFCCounter'}
count={this.state.count}
onIncrement={() => { this.setState({ count: this.state.count + 1 }); }}
/>
);
}
}
- 使用default props
// 定义
import * as React from 'react';
type Props = {
label: string;
initialCount: number;
};
type State = {
count: number;
};
export class ClassCounterWithDefaultProps extends React.Component<
Props,
State
> {
static defaultProps = { // 定义默认props
initialCount: 0,
};
readonly state: State = {
count: this.props.initialCount,
};
handleIncrement = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
const { handleIncrement } = this;
const { label } = this.props;
const { count } = this.state;
return (
<div>
<span>
{label}: {count}
</span>
<button type="button" onClick={handleIncrement}>
{`Increment`}
</button>
</div>
);
}
}
import * as React from 'react';
import { StatefulCounterWithDefault } from '@src/components';
export default () => (
<StatefulCounterWithDefault
label={'StatefulCounter'}
/>
);
Generic Components
泛型组件
- 轻松创建泛型组件的变体,并复用公共逻辑
- 常见用例:泛型list组件
// 定义
import * as React from 'react';
export interface GenericListProps<T> {
items: T[];
itemRenderer: (item: T) => JSX.Element;
}
export class GenericList<T> extends React.Component<GenericListProps<T>, {}> {
render() {
const { items, itemRenderer } = this.props;
return (
<div>
{items.map(itemRenderer)}
</div>
);
}
}
// 使用
import * as React from 'react';
import { IUser, User } from '@src/models';
import { GenericList } from '@src/components';
const users = [
new User('Rosamonte', 'Especial'),
new User('Aguantadora', 'Despalada'),
new User('Taragui', 'Vitality'),
];
export class UserList extends GenericList<IUser> { }
export default () => (
<UserList
items={users}
itemRenderer={(item) => <div key={item.id}>{item.fullName}</div>}
/>
);
Render Props
// 定义
import * as React from 'react';
export interface MouseProviderProps {
render: (state: MouseProviderState) => React.ReactNode;
}
interface MouseProviderState {
readonly x: number;
readonly y: number;
}
export class MouseProvider extends React.Component<MouseProviderProps, MouseProviderState> {
readonly state: MouseProviderState = { x: 0, y: 0 };
handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
this.setState({
x: event.clientX,
y: event.clientY,
});
};
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/*
Instead of providing a static representation of what <Mouse> renders,
use the `render` prop to dynamically determine what to render.
*/}
{this.props.render(this.state)}
</div>
);
}
}
// 使用
import * as React from 'react';
import { MouseProvider } from './mouse-provider';
export default () => (
<MouseProvider
render={mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}
/>
);
Higher-Order Components(高阶组件)
HOC知识
-
什么是高阶组件?
高阶组件就是将组件作为参数传入,并返回新组件的函数;
-
高阶组件有什么作用?
将公共的部分抽离出来,实现在许多组件之间可以共享。
HOC包裹的组件
import React from 'react';
import { Diff } from 'utility-types';
// These props will be injected into the base component
interface InjectedProps {
count: number;
onIncrement: () => void;
}
export const withState = <BaseProps extends InjectedProps>(
BaseComponent: React.ComponentType<BaseProps>
) => {
type HocProps = Diff<BaseProps, InjectedProps> & {
// here you can extend hoc with new props
initialCount?: number;
};
type HocState = {
readonly count: number;
};
return class Hoc extends React.Component<HocProps, HocState> {
// Enhance component name for debugging and React-Dev-Tools
static displayName = `withState(${BaseComponent.name})`;
// reference to original wrapped component
static readonly WrappedComponent = BaseComponent;
readonly state: HocState = {
count: Number(this.props.initialCount) || 0,
};
handleIncrement = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
const { ...restProps } = this.props;
const { count } = this.state;
return (
<BaseComponent
count={count} // injected
onIncrement={this.handleIncrement} // injected
{...(restProps as BaseProps)}
/>
);
}
};
};
import * as React from 'react';
import { withState } from '../hoc';
import { FCCounter } from '../components';
const FCCounterWithState = withState(FCCounter);
export default () => <FCCounterWithState label={'FCCounterWithState'} />;
HOC包裹组件并且注入props
import React from 'react';
const MISSING_ERROR = 'Error was swallowed during propagation.';
export const withErrorBoundary = <BaseProps extends {}>(
BaseComponent: React.ComponentType<BaseProps>
) => {
type HocProps = {
// here you can extend hoc with new props
};
type HocState = {
readonly error: Error | null | undefined;
};
return class Hoc extends React.Component<HocProps, HocState> {
// Enhance component name for debugging and React-Dev-Tools
static displayName = `withErrorBoundary(${BaseComponent.name})`;
// reference to original wrapped component
static readonly WrappedComponent = BaseComponent;
readonly state: HocState = {
error: undefined,
};
componentDidCatch(error: Error | null, info: object) {
this.setState({ error: error || new Error(MISSING_ERROR) });
this.logErrorToCloud(error, info);
}
logErrorToCloud = (error: Error | null, info: object) => {
// TODO: send error report to service provider
};
render() {
const { children, ...restProps } = this.props;
const { error } = this.state;
if (error) {
return <BaseComponent {...(restProps as BaseProps)} />;
}
return children;
}
};
};
import React, {useState} from 'react';
import { withErrorBoundary } from '../hoc';
import { ErrorMessage } from '../components';
const ErrorMessageWithErrorBoundary =
withErrorBoundary(ErrorMessage);
const BrokenComponent = () => {
throw new Error('I\'m broken! Don\'t render me.');
};
const BrokenButton = () => {
const [shouldRenderBrokenComponent, setShouldRenderBrokenComponent] =
useState(false);
if (shouldRenderBrokenComponent) {
return <BrokenComponent />;
}
return (
<button
type="button"
onClick={() => {
setShouldRenderBrokenComponent(true);
}}
>
{`Throw nasty error`}
</button>
);
};
export default () => (
<ErrorMessageWithErrorBoundary>
<BrokenButton />
</ErrorMessageWithErrorBoundary>
);
待续
相关文档