Skip to content

Commit bb18d84

Browse files
swyxioswyx
and
swyx
authored
address old issues in cheatsheet (#275)
* batch1 * make age optional in defaultprops example * talk about prop-types * add useimperativehandle Co-authored-by: swyx <wanshawn@amazon.com>
1 parent 928de8d commit bb18d84

File tree

5 files changed

+100
-65
lines changed

5 files changed

+100
-65
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -2405,6 +2405,7 @@ It is worth mentioning some resources to help you get started:
24052405
- Marius Schultz: https://blog.mariusschulz.com/series/typescript-evolution with an [Egghead.io course](https://egghead.io/courses/advanced-static-types-in-typescript)
24062406
- Basarat's Deep Dive: https://basarat.gitbook.io/typescript/
24072407
- Rares Matei: [Egghead.io course](https://egghead.io/courses/practical-advanced-typescript)'s advanced TypeScript course on Egghead.io is great for newer typescript features and practical type logic applications (e.g. recursively making all properties of a type `readonly`)
2408+
- Go through [Remo Jansen's TypeScript ladder](http://www.techladder.io/?tech=typescript)
24082409
- Shu Uesugi: [TypeScript for Beginner Programmers](https://ts.chibicode.com/)
24092410
24102411
<!--END-SECTION:learn-ts-->

docs/advanced/patterns_by_usecase.md

+18
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,24 @@ type NumbersChildren = number[];
315315
type TwoNumbersChildren = [number, number];
316316
```
317317

318+
<details>
319+
<summary>
320+
Don't forget that you can also use `prop-types` if TS fails you.
321+
</summary>
322+
323+
```ts
324+
Parent.propTypes = {
325+
children: PropTypes.shape({
326+
props: PropTypes.shape({
327+
// could share `propTypes` to the child
328+
value: PropTypes.string.isRequired,
329+
}),
330+
}).isRequired,
331+
};
332+
```
333+
334+
</details>
335+
318336
### What You CANNOT Do
319337

320338
The thing you cannot do is **specify which components** the children are, e.g. If you want to express the fact that "React Router `<Routes>` can only have `<Route>` as children, nothing else is allowed" in TypeScript.

docs/basic/getting-started/default-props.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ const Greet = ({ age = 21 }: GreetProps) => {
6565
// class components
6666
// ////////////////
6767
type GreetProps = {
68-
age: number;
68+
age?: number;
6969
};
7070

7171
class Greet extends React.Component<GreetProps> {
@@ -125,7 +125,7 @@ export class MyComponent extends React.Component<IMyComponentProps> {
125125

126126
The problem with this approach is it causes complex issues with the type inference working with `JSX.LibraryManagedAttributes`. Basically it causes the compiler to think that when creating a JSX expression with that component, that all of its props are optional.
127127

128-
[See commentary by @ferdaber here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57).
128+
[See commentary by @ferdaber here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57) and [here](https://github.com/typescript-cheatsheets/react/issues/61).
129129

130130
</details>
131131

docs/basic/getting-started/hooks.md

+78-62
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ title: Hooks
55

66
Hooks are [supported in `@types/react` from v16.8 up](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a05cc538a42243c632f054e42eab483ebf1560ab/types/react/index.d.ts#L800-L1031).
77

8-
**useState**
8+
## useState
99

1010
Type inference works very well most of the time:
1111

@@ -24,37 +24,53 @@ const [user, setUser] = React.useState<IUser | null>(null);
2424
setUser(newUser);
2525
```
2626

27-
**useRef**
27+
## useReducer
2828

29-
When using `useRef`, you have two options when creating a ref container that does not have an initial value:
29+
You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions) for reducer actions. Don't forget to define the return type of reducer, otherwise TypeScript will infer it.
3030

31-
```ts
32-
const ref1 = useRef<HTMLElement>(null!);
33-
const ref2 = useRef<HTMLElement | null>(null);
31+
```tsx
32+
type AppState = {};
33+
type Action =
34+
| { type: "SET_ONE"; payload: string } // typescript union types allow for leading |'s to have nicer layout
35+
| { type: "SET_TWO"; payload: number };
36+
37+
export function reducer(state: AppState, action: Action): AppState {
38+
switch (action.type) {
39+
case "SET_ONE":
40+
return {
41+
...state,
42+
one: action.payload, // `payload` is string
43+
};
44+
case "SET_TWO":
45+
return {
46+
...state,
47+
two: action.payload, // `payload` is number
48+
};
49+
default:
50+
return state;
51+
}
52+
}
3453
```
3554

36-
The first option will make `ref1.current` read-only, and is intended to be passed in to built-in `ref` attributes that React will manage (because React handles setting the `current` value for you).
55+
[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/C4TwDgpgBAgmYGVgENjQLxQN4F8CwAUKJLAMbACWA9gHZTqFRQA+2UxEAXFAEQICiAFQD6AeQBy-HgG4oYZCAA2VZABNuAZ2AAnCjQDmUfASass7cF14CRggOqiZchcrXcaAVwC2AIwjajaUJCCAAPMCptYCgAMw8acmo6bQhVD1J-AAotVCs4RBQ0ABooZETabhhymgBKSvgkXOxGKA0AdwpgUgALKEyyyloAOg4a5pMmKFJkDWg+ITFJHk4WyagU4A9tOixVtaghw5zivbXaKwGkofklFVUoAHoHqAADG9dVF6gKDVadPX0p0Ce2ms2sC3sjhWEzWGy2OyBTEOQ2OECKiPYbSo3Euw3ed0ezzeLjuXx+UE8vn8QJwQRhUFUEBiyA8imA0P26wgm22f1ydKYxhwQA)
3756

3857
<details>
39-
<summary>What is the <code>!</code> at the end of <code>null!</code>?</summary>
4058

41-
`null!` is a non-null assertion operator (the `!`). It asserts that any expression before it is not `null` or `undefined`, so if you have `useRef<HTMLElement>(null!)` it means that you're instantiating the ref with a current value of `null` but lying to TypeScript that it's not `null`.
59+
<summary><b>Usage with `Reducer` from `redux`</b></summary>
4260

43-
```ts
44-
function MyComponent() {
45-
const ref1 = useRef<HTMLElement>(null!);
46-
useEffect(() => {
47-
doSomethingWith(ref1.current); // TypeScript won't require null-check e.g. ref1 && ref1.current
48-
});
49-
return <div ref={ref1}> etc </div>;
50-
}
61+
In case you use the [redux](https://github.com/reduxjs/redux) library to write reducer function, It provides a convenient helper of the format `Reducer<State, Action>` which takes care of the return type for you.
62+
63+
So the above reducer example becomes:
64+
65+
```tsx
66+
import { Reducer } from 'redux';
67+
68+
export function reducer: Reducer<AppState, Action>() {}
5169
```
5270

5371
</details>
5472

55-
The second option will make `ref2.current` mutable, and is intended for "instance variables" that you manage yourself.
56-
57-
**useEffect**
73+
## useEffect
5874

5975
When using `useEffect`, take care not to return anything other than a function or `undefined`, otherwise both TypeScript and React will yell at you. This can be subtle when using arrow functions:
6076

@@ -73,7 +89,35 @@ function DelayedEffect(props: { timerMs: number }) {
7389
}
7490
```
7591

76-
**useRef**
92+
## useRef
93+
94+
When using `useRef`, you have two options when creating a ref container that does not have an initial value:
95+
96+
```ts
97+
const ref1 = useRef<HTMLElement>(null!);
98+
const ref2 = useRef<HTMLElement | null>(null);
99+
```
100+
101+
The first option will make `ref1.current` read-only, and is intended to be passed in to built-in `ref` attributes that React will manage (because React handles setting the `current` value for you).
102+
103+
<details>
104+
<summary>What is the <code>!</code> at the end of <code>null!</code>?</summary>
105+
106+
`null!` is a non-null assertion operator (the `!`). It asserts that any expression before it is not `null` or `undefined`, so if you have `useRef<HTMLElement>(null!)` it means that you're instantiating the ref with a current value of `null` but lying to TypeScript that it's not `null`.
107+
108+
```ts
109+
function MyComponent() {
110+
const ref1 = useRef<HTMLElement>(null!);
111+
useEffect(() => {
112+
doSomethingWith(ref1.current); // TypeScript won't require null-check e.g. ref1 && ref1.current
113+
});
114+
return <div ref={ref1}> etc </div>;
115+
}
116+
```
117+
118+
</details>
119+
120+
The second option will make `ref2.current` mutable, and is intended for "instance variables" that you manage yourself.
77121

78122
```tsx
79123
function TextInputWithFocusButton() {
@@ -101,53 +145,25 @@ function TextInputWithFocusButton() {
101145

102146
example from [Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#useref)
103147

104-
**useReducer**
148+
## useImperativeHandle
105149

106-
You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions) for reducer actions. Don't forget to define the return type of reducer, otherwise TypeScript will infer it.
150+
_we dont have much here, but this is from [a discussion in our issues](https://github.com/typescript-cheatsheets/react/issues/106)_
107151

108152
```tsx
109-
type AppState = {};
110-
type Action =
111-
| { type: "SET_ONE"; payload: string } // typescript union types allow for leading |'s to have nicer layout
112-
| { type: "SET_TWO"; payload: number };
113-
114-
export function reducer(state: AppState, action: Action): AppState {
115-
switch (action.type) {
116-
case "SET_ONE":
117-
return {
118-
...state,
119-
one: action.payload, // `payload` is string
120-
};
121-
case "SET_TWO":
122-
return {
123-
...state,
124-
two: action.payload, // `payload` is number
125-
};
126-
default:
127-
return state;
128-
}
153+
type ListProps<ItemType> = {
154+
items: ItemType[];
155+
innerRef?: React.Ref<{ scrollToItem(item: ItemType): void }>;
156+
};
157+
158+
function List<ItemType>(props: ListProps<ItemType>) {
159+
useImperativeHandle(props.innerRef, () => ({
160+
scrollToItem() {},
161+
}));
162+
return null;
129163
}
130164
```
131165

132-
[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/C4TwDgpgBAgmYGVgENjQLxQN4F8CwAUKJLAMbACWA9gHZTqFRQA+2UxEAXFAEQICiAFQD6AeQBy-HgG4oYZCAA2VZABNuAZ2AAnCjQDmUfASass7cF14CRggOqiZchcrXcaAVwC2AIwjajaUJCCAAPMCptYCgAMw8acmo6bQhVD1J-AAotVCs4RBQ0ABooZETabhhymgBKSvgkXOxGKA0AdwpgUgALKEyyyloAOg4a5pMmKFJkDWg+ITFJHk4WyagU4A9tOixVtaghw5zivbXaKwGkofklFVUoAHoHqAADG9dVF6gKDVadPX0p0Ce2ms2sC3sjhWEzWGy2OyBTEOQ2OECKiPYbSo3Euw3ed0ezzeLjuXx+UE8vn8QJwQRhUFUEBiyA8imA0P26wgm22f1ydKYxhwQA)
133-
134-
<details>
135-
136-
<summary><b>Usage with `Reducer` from `redux`</b></summary>
137-
138-
In case you use the [redux](https://github.com/reduxjs/redux) library to write reducer function, It provides a convenient helper of the format `Reducer<State, Action>` which takes care of the return type for you.
139-
140-
So the above reducer example becomes:
141-
142-
```tsx
143-
import { Reducer } from 'redux';
144-
145-
export function reducer: Reducer<AppState, Action>() {}
146-
```
147-
148-
</details>
149-
150-
**Custom Hooks**
166+
## Custom Hooks
151167

152168
If you are returning an array in your Custom Hook, you will want to avoid type inference as TypeScript will infer a union type (when you actually want different types in each position of the array). Instead, use [TS 3.4 const assertions](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/#const-assertions):
153169

docs/hoc/full-example.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,4 @@ export function inject<TProps, TInjectedKeys extends keyof TProps>(
9292

9393
### Using `forwardRef`
9494

95-
For "true" reusability you should also consider exposing a ref for your HOC. You can use `React.forwardRef<Ref, Props>` as documented in [the basic cheatsheet](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/README.md#forwardrefcreateref), but we are interested in more real world examples. [Here is a nice example in practice](https://gist.github.com/OliverJAsh/d2f462b03b3e6c24f5588ca7915d010e) from @OliverJAsh.
95+
For "true" reusability you should also consider exposing a ref for your HOC. You can use `React.forwardRef<Ref, Props>` as documented in [the basic cheatsheet](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/README.md#forwardrefcreateref), but we are interested in more real world examples. [Here is a nice example in practice](https://gist.github.com/OliverJAsh/d2f462b03b3e6c24f5588ca7915d010e) from @OliverJAsh (note - it still has some rough edges, we need help to test this out/document this).

0 commit comments

Comments
 (0)