Typescript-React Cheatsheet
文章目录

References

Frequently Used

Function Components

FC 必须要返回 JSX, 不能返回其他类型

Quick Snippet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type AppProps = {message: string}; /* could also use interface */

// Type 1
const App = ({message}: AppProps) => <div>{message}</div>;

// Type 2
const App: React.FC<{message: string}> = ({ message }) => (
<div>{message}</div>
);

// Type 3
const App: React.FunctionComponent<{message: string}> = ({ message }) => (
<div>{message}</div>
);

Full Example

1
2
3
4
5
6
7
8
9
10
export interface LiquidDatePickerInput extends InputBaseProps {
selected?: Date
onChange?: (date: any) => void
}

const DatePicker: React.FC<DatePickerInput> = props => (
<DatePicker
{...props}
/>
)

Convert other HTML to JSX

有些时候需要用一些奇怪的方法试着一堆 HTML, 如果希望 react 做这样的操作,那么就要进行转换

这是一个临时的 Solution

See commentary by @ferdaber here.

1
const MyArrayComponent = () => (Array(5).fill(<div />) as any) as JSX.Element;

Hooks

useState

1
2
3
4
5
// Hooks 也可以设置类型, 但是因为会有一个空值所以需要使 Union Type
const [user, setUser] = React.useState<IUser | null>(null);

// newUser 必须是 IUser 的类型
setUser(newUser);

useRef

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function TextInputWithFocusButton() {

// initialise with null, but tell TypeScript we are looking for an HTMLInputElement
// HTMLInputElement 类型非常重要
const inputEl = React.useRef<HTMLInputElement>(null);

const onButtonClick = () => {
// null check
if (inputEl && inputEl?.current) {
inputEl.current.focus();
}
};
return (
<>
{/* inputEl 设置了仅仅针对 HTMLInputElement 类型, 因此只能给 input 使用 */}
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}

useReducer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type AppState = {};
type Action =
| {type: "SET_ONE"; payload: string}
| {type: "SET_TWO"; payload: number};

export function reducer(state: AppState, action: Action): AppState {
switch (action.type) {
case "SET_ONE":
return {
...state,
one: action.payload, // `payload` is string
};
case "SET_TWO":
return {
...state,
two: action.payload, // `payload` is number
};
default:
return state;
}
}

Custom Hooks

1
2
3
4
5
6
7
8
export function useLoading() {
const [isLoading, setState] = React.useState(false);
const load = (aPromise: Promise<any>) => {
setState(true);
return aPromise.finally(() => setState(false));
};
return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[]
}

Class Components

Standard Class Component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* 这些接口加上 可以同时 export 出去以方便后期复用 */
type MyProps = {
// using `interface` is also ok
message: string;
};

type MyState = {
count: number; // like this
};
class App extends React.Component<MyProps, MyState> {
state: MyState = {
// optional second annotation for better type inference
count: 0,
};

pointer: number;

componentDidMount() {
this.pointer = 3;
}

render() {
return (
<div onClick={() => this.increment(1)}>
{this.props.message} {/* props */}
{this.state.count} {/* states */}
{this.pointer} {/* attrs */}
</div>
);
}
increment = (amt: number) => {
// like this
this.setState((state) => ({
count: state.count + amt,
}));
};

}

Types & Interfaces

使用 Interface 的情况:

  1. 公开的 API
  2. 三方的 API

使用 Type 的情况:

  1. Component 的 Props 和 States
  2. 经常需要扩展的类型 (后期会使用 type MyType = TypeA | TypeB 进行扩展)

Examples: Basic Prop Types + React Prop Type

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
type AppProps = {
message: string;
count: number;
disabled: boolean;
/** array of a type! */
names: string[];
/** string literals to specify exact string values, with a union type to join them together */
status: "waiting" | "success";
/** any object as long as you dont use its properties (not common) */
obj: object;
obj2: {}; // almost the same as `object` , exactly the same as `Object`
/** an object with defined properties (preferred) */
obj3: {
id: string;
title: string;
};
/** array of objects! (common) */
objArr: {
id: string;
title: string;
}[];
/** any function as long as you don't invoke it (not recommended) */
onSomething: Function;
/** function that doesn't take or return anything (VERY COMMON) */
onClick: () => void;
/** function with named prop (VERY COMMON) */
onChange: (id: number) => void;
/** alternative function type syntax that takes an event (VERY COMMON) */
onClick(event: React.MouseEvent<HTMLButtonElement>): void;
/** an optional prop (VERY COMMON!) */
optional?: OptionalType;
};

export declare interface AppProps {
children1: JSX.Element; // bad, doesnt account for arrays
children2: JSX.Element | JSX.Element[]; // meh, doesn't accept strings
children3: React.ReactChildren; // despite the name, not at all an appropriate type; it is a utility
children4: React.ReactChild[]; // better
children: React.ReactNode; // best, accepts everything
functionChildren: (name: string) => React.ReactNode; // recommended function as a child render prop type
style?: React.CSSProperties; // to pass through style props
onChange?: React.FormEventHandler<HTMLInputElement>; // form events! the generic parameter is the type of event.target
props: Props & React.PropsWithoutRef<JSX.IntrinsicElements["button"]>; // to impersonate all the props of a button element without its ref
}

/**
JSX.Element -> Return value of React.createElement
React.ReactNode -> Return value of a component
*/

Forms and Events

Inline Event

onClick Event

1
2
3
4
5
6
7
const el = (
<button
onClick={(event) => {
/* ... */
}}
/>
);

Uncontrolled Form Event

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<form
ref={formRef}
onSubmit={(e: React.SyntheticEvent) => { // 写的时候需要注意这边参数的 type
e.preventDefault();
const target = e.target as typeof e.target & {
email: {value: string};
password: {value: string};
};
const email = target.email.value; // typechecks!
const password = target.password.value; // typechecks!
// etc...
}}
>
<div>
<label>
Email:
<input type="email" name="email" />
</label>
</div>
<div>
<label>
Password:
<input type="password" name="password" />
</label>
</div>
<div>
<input type="submit" value="Log in" />
</div>
</form>

Event Types

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

onChange = (e: React.FormEvent<HTMLInputElement>): void => {
this.setState({text: e.currentTarget.value});
};

// 下面这种方法也可以,这是两种不同的 typecheck

onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
this.setState({text: e.currentTarget.value})
}

render() {
return (
<div>
<input type="text" value={this.state.text} onChange={this.onChange} />
</div>
);
}

TODO: https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#context