More React Typescript notes

This commit is contained in:
thomasabishop 2022-08-04 12:11:33 +01:00
parent d6bd380bc2
commit 523b6a4a7d
5 changed files with 241 additions and 42 deletions

View file

@ -0,0 +1,99 @@
---
tags:
- Programming_Languages
- typescript
- react
- react-hooks
---
# Typing built-in hooks
## `useState`
```tsx
const [amount, setAmount] = useState<number | string>(3);
```
### With a ustom type
```tsx
interface IState {
people: IPerson[];
}
interface IPerson {
name: string;
age: number;
}
const [people, setPeople] = useState<IState>({});
// Alternative declaration
interface IState {
people: {
name: string;
age: number;
}[];
}
const [people, setPeople] = useState<IState['people']>({});
```
## `useReducer`
Mostly the same. They key thing to type is the reducer function itself. You don't need to type the intitial state unless it is a pre-existing type, you can just use type assertion:
```jsx
interface IAction {
| { type: "increment"; payload: number }
| { type: "decrement"; payload: string };
}
const initialState = { count: 0 };
function reducer(state: typeof initialState, action: IAction) {
switch (action.type) {
case "increment":
return { count: state.count + action.payload };
case "decrement":
return { count: state.count - Number(action.payload) };
default:
throw new Error();
}
}
```
Then the syntax is as normal, i.e:
```js
const [state, dispatch] = useReducer(reducer, initialState);
```
## `useEffect`
> Limited potential for typing here as this hook does not return a value. See if there is useful inferred typing from use and update.
## `useContext`
We can use generics but mostly this is untyped:
```jsx
type Theme = 'light' | 'dark';
const ThemeContext = createContext < Theme > 'dark';
const App = () => (
<ThemeContext.Provider value="dark">
<MyComponent />
</ThemeContext.Provider>
);
const MyComponent = () => {
const theme = useContext(ThemeContext);
return <div>The theme is {theme}</div>;
};
```
## `useCallback` / `useMemo`
You must only type the parameters that are passed to the callback, the return value will be inferred.

View file

@ -0,0 +1,27 @@
---
tags:
- Programming_Languages
- typescript
- react
---
# Components
We write functional components in the manner of a normal [function](/Programming_Languages/React/React_Typescript/Functions.md) that take a props argument and return a JSX element.
```jsx
interface IProps = {
message: string;
};
const App = ({message}: IProps): JSX.Element => <div>{message}</div>;
```
## `React.FunctionComponent` / `React.FC`
It used to be the case that you would type a function component like so:
```jsx
const App: React.FunctionComponent<{message: string}> = ({message}) => <div>{message}</div>;
```
This is now discouraged as it means the component can always accept children, even when you do not want this to be the case. There are several other minor drawerbacks but it's generally best to leave out the declaration.

View file

@ -33,7 +33,7 @@ interface IProps {
setPeople: React.Dispatch<React.SetStateAction<Props["people"]>>
}
const AddToList: React.FC<IProps> = () => {
const AddToList = () => {
const [people, setPeople] = useState<IState["people"]>({})
const [formVals, setFormVals] = useState({});
@ -62,7 +62,7 @@ return (
<input type="text" name="age" value={input.age} onChange={handleChange} />
</form>
<button onClick={handleClick}>Add to list</button>
);
);
};
```
@ -71,3 +71,24 @@ This follows standard practise for [controlled-components](/Programming_Language
- We define the change event as being of the type `React.ChangeEvent` and state that it corresponds to a generic - `HTMLInputElement`. So we are saying that whenever this function is called we must be passing it an input element so that we can extract the event associated with its `target` property.
- We are passing around variations on the `IState` interface in order to type the values that we are adding to the people array.
## Further standard types for event handling
### onClick
```tsx
handleClick(event: MouseEvent<HTMLButtonElement>) {
event.preventDefault();
alert(event.currentTarget.tagName);
}
```
### onSubmit
```tsx
handleSubmit(e: React.SyntheticEvent) {
event.preventDefault();
}
```
> Most event types and their associated generics will be revealed by VS Code Intellisense so you don't need to memorize them all

View file

@ -1,40 +0,0 @@
---
tags:
- Programming_Languages
- typescript
- react
---
# Managing state
## Basic: `useState`
```tsx
const [amount, setAmount] = useState<number | string>(3);
```
### Custom type
```tsx
interface IState {
people: IPerson[];
}
interface IPerson {
name: string;
age: number;
}
const [people, setPeople] = useState<IState>({});
// Alternative declaration
interface IState {
people: {
name: string;
age: number;
}[];
}
const [people, setPeople] = useState<IState['people']>({});
```

View file

@ -7,6 +7,46 @@ tags:
# Props
## Types or interfaces?
They are mostly interchangeable but there are some specific differences
### Interfaces
- Classes and other interfaces can extend an interface but not a type
```ts
interface Animal {
name: string;
}
interface Bear extends Animal {
honey: boolean;
}
```
- You can add properties to an interface without generating a double declaration error:
```ts
interface Window {
title: string;
}
interface Window {
ts: TypeScriptAPI;
}
```
### Types
You can create unions with types but not interfaces.
> The consensus seems to be that interfaces should be used over types unless there are ocassions when you do not want a type to be extendable / redeclarable. Or when you want to use unions.
## How we type props
The custom is to type props as an interface. This way we can easily type function prop types.
```tsx
interface IProps {
people: {
@ -35,4 +75,56 @@ const [people, setPeople] = useState<IState['people']>({});
```
## Common types for props
### Object with any number of properties
```tsx
interface IProps {
obj: {};
}
```
### Array of objects
```tsx
interface IProps {
val1: string;
val2: boolean;
}
[];
```
### Functions
```tsx
// Function that doesn't receive or return anything
interface IProps {
onClick: () => void;
}[]
// Function with named parameter
interface IProps {
onClick: (id: number) => void;
}[]
// Function that takes an event
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
```
### Other React components
```js
interface IProps {
children: React.ReactNode;
}
interface IProps {
children: JSX.Element;
}
```
`React.ReactNode` covers everything that React can render. This should be definitely used when the prop is a child component, otherwise `JSX.element` is ok.
<p style="color: red;">Should I use type or interface? What is consensus?</p>