Skip to content

Commit 26110ee

Browse files
committed
Add React hooks
1 parent f6597ed commit 26110ee

File tree

2 files changed

+123
-3
lines changed

2 files changed

+123
-3
lines changed

src/pages/examples/functional-components.mdx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import SEO from '../../components/seo';
1313
/>
1414

1515
# Functional Components
16+
1617
Given that functional components are just functions that take in props as a parameter, we can do the following:
1718

18-
```tsx
19+
```typescript
1920
type HelloWorldProps = {
2021
userName: string;
2122
};
@@ -27,7 +28,7 @@ const HelloWorld = ({ userName }: HelloWorldProps) => (
2728

2829
However, this does not take the `children` prop into account. Use the `React.FC` type and define the generic as your props type.
2930

30-
```tsx
31+
```typescript
3132
type HelloWorldProps = {
3233
userName: string;
3334
};
@@ -37,5 +38,5 @@ const HelloWorld: React.FC<HelloWorldProps> = ({ children, userName }) => (
3738
<p>Hello, {userName}</p>
3839
{children}
3940
</div>
40-
)
41+
);
4142
```

src/pages/examples/hooks.mdx

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
---
2+
name: Hooks
3+
route: /examples/hooks
4+
menu: Examples
5+
---
6+
7+
import SEO from '../../components/seo';
8+
9+
<SEO
10+
description="Using TypeScript with React hooks."
11+
title="Hooks"
12+
keywords={['hooks']}
13+
/>
14+
15+
# Hooks
16+
17+
## useState
18+
19+
Type inference works when state is initialized to a non-null value:
20+
21+
```typescript
22+
const [value, setValue] = useState('initial state');
23+
```
24+
25+
For non-null values:
26+
27+
```typescript
28+
const [value, setValue] = useState<string | null>('initial state');
29+
```
30+
31+
## useEffect
32+
33+
Like `useState`, type inference works for `useEffect`. Please note that you must return a function or `undefined` for type inference to work correctly. As described in [Trey Huffine](https://levelup.gitconnected.com/@treyhuffine)'s [useTypescript — A Complete Guide to React Hooks and TypeScript](https://levelup.gitconnected.com/usetypescript-a-complete-guide-to-react-hooks-and-typescript-db1858d1fb9c):
34+
35+
```typescript
36+
// THIS IS INCORRECT | READ ABOVE AND DO NOT USE
37+
function DelayedEffect(props: { timerMs: number }) {
38+
const { timerMs } = props;
39+
useEffect(
40+
// ** BAD! ** setTimeout implicitly returns a number because the arrow function body isn't wrapped in curly braces
41+
() =>
42+
setTimeout(() => {
43+
/* do stuff */
44+
}, timerMs),
45+
[timerMs]
46+
);
47+
return null;
48+
}
49+
```
50+
51+
## useRef
52+
53+
Like the two hooks above, type inference works for `useRef`. When creating a ref container that does not have an initial value:
54+
55+
```typescript
56+
const ref = useRef<HTMLElement | null>(null);
57+
```
58+
59+
## useReducer
60+
61+
The following `useReducer` example is from the [React docs](https://reactjs.org/docs/hooks-reference.html#usereducer) (with some added code to introduce complexity). Please note the use of [discriminated unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions) when defining `Action`. This is necessary to define different `payload` types for different `type`s of actions.
62+
63+
```typescript
64+
enum ActionType {
65+
Increment = 'increment',
66+
Decrement = 'decrement',
67+
ModifyLastButtonPressed = 'modify_last_button_pressed',
68+
}
69+
70+
type AppState = {
71+
count: number;
72+
lastButtonPressed: string;
73+
};
74+
75+
type Action =
76+
| { type: ActionType.Increment | ActionType.Decrement; payload: number }
77+
| { type: ActionType.ModifyLastButtonPressed; payload: string };
78+
79+
const initialState = { count: 0, lastButtonPressed: '' };
80+
81+
const reducer: React.Reducer<AppState, Action> = (state, action) => {
82+
switch (action.type) {
83+
case ActionType.Increment:
84+
return { ...state, count: state.count + action.payload };
85+
case ActionType.Decrement:
86+
return { ...state, count: state.count - action.payload };
87+
case ActionType.ModifyLastButtonPressed:
88+
return { ...state, lastButtonPressed: action.payload };
89+
default:
90+
throw new Error();
91+
}
92+
};
93+
94+
function Counter({ initialState }) {
95+
const [state, dispatch] = useReducer(reducer, initialState);
96+
return (
97+
<React.Fragment>
98+
Count: {state.count} You last pressed: {state.lastButtonPressed}
99+
<br />
100+
<button
101+
onClick={() => {
102+
dispatch({ type: ActionType.Increment, payload: 1 });
103+
dispatch({ type: ActionType.ModifyLastButtonPressed, payload: '+' });
104+
}}
105+
>
106+
+
107+
</button>
108+
<button
109+
onClick={() => {
110+
dispatch({ type: ActionType.Decrement, payload: 1 });
111+
dispatch({ type: ActionType.ModifyLastButtonPressed, payload: '-' });
112+
}}
113+
>
114+
-
115+
</button>
116+
</React.Fragment>
117+
);
118+
}
119+
```

0 commit comments

Comments
 (0)