Repository: ibnumusyaffa/simple-react-typescript-cheatsheet
Branch: main
Commit: 816d4f8eef95
Files: 1
Total size: 11.0 KB
Directory structure:
gitextract_6serk1rq/
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: README.md
================================================
# Simple React Typescript Cheatsheet
- [Simple React Typescript Cheatsheet](#simple-react-typescript-cheatsheet)
- [Typing props with inline type](#typing-props-with-inline-type)
- [Typing props with Type](#typing-props-with-type)
- [Typing props with default value](#typing-props-with-default-value)
- [Typing props with children](#typing-props-with-children)
- [Using Native HTML props to React Components](#using-native-html-props-to-react-components)
- [1. Basic](#1-basic)
- [2. Combine with your type](#2-combine-with-your-type)
- [3. Overriding Native Props](#3-overriding-native-props)
- [4. Extracting Props from Custom Components](#4-extracting-props-from-custom-components)
- [Typing Event Handlers from native element](#typing-event-handlers-from-native-element)
- [useState](#usestate)
- [useCallback](#usecallback)
- [useRef](#useref)
- [Basic useRef](#basic-useref)
- [useRef with HTML element](#useref-with-html-element)
- [useRef with forwardRef](#useref-with-forwardref)
- [Making a Read-Only Ref Mutable](#making-a-read-only-ref-mutable)
- [useReducer](#usereducer)
- [Context](#context)
- [Polymorphic](#polymorphic)
- [Types or Interfaces?](#types-or-interfaces)
- [Resources](#resources)
## Typing props with inline type
```tsx
function Button(props: { children: React.ReactNode }) {
return ;
}
```
## Typing props with Type
```tsx
// you can use interface too
type ButtonProps = {
className: string;
children: React.ReactNode;
};
function Button(props: ButtonProps) {
return ;
}
// with destructuring
function OtherButton({ className, ...props }: ButtonProps) {
return ;
}
```
## Typing props with default value
```tsx
type ButtonProps = {
disabled?: boolean;
className: string;
children: React.ReactNode;
};
function Button({ disabled = true, ...props }: ButtonProps) {
return (
);
}
```
## Typing props with children
```tsx
type ButtonProps = {
// accept everything React can render
children: React.ReactNode;
};
function Button(props: ButtonProps) {
return ;
}
```
## Using Native HTML props to React Components
### 1. Basic
```tsx
import React, { ComponentProps } from "react";
//ComponentProps<"button"> : get all type/props from native button element
function Button(props: ComponentProps<"button">) {
return ;
}
```
### 2. Combine with your type
```tsx
import React, { ComponentProps } from "react";
type ButtonProps = ComponentProps<"button"> & {
variant: "primary" | "secondary";
};
function Button(props: ButtonProps) {
return ;
}
```
### 3. Overriding Native Props
```tsx
import React, { ComponentProps } from "react";
//remove onChange property from input with Omit and combine with new type
type InputProps = Omit, "onChange"> & {
onChange: (value: string) => void;
};
function Input(props: InputProps) {
return ;
}
```
### 4. Extracting Props from Custom Components
Useful when author of some external library dont export the type definition
```tsx
import { ComponentProps } from "react";
import { Navbar } from "some-ui-library";
type NavBarProps = ComponentProps;
```
## Typing Event Handlers from native element
Hover native html props in VSCode, you can copy paste the type definition
```tsx
type ButtonProps = {
other?: Boolean;
onClick?: React.MouseEventHandler;
};
```

## useState
```tsx
// ❌ Typescript already know `text` type is string
const [text, setText] = useState("");
// ✅ no need to tell typescript, only work with primitive value
const [text, setText] = useState("");
```
```tsx
type Tag = {
id: number;
value: string;
};
const [tags, setTags] = useState([]);
```
```tsx
// data : Data | undefined
const [data, setData] = useState();
// data : Data | undefined
const [data, setData] = useState(undefined);
// data : Data | null
const [data, setData] = useState(null);
```
## useCallback
```tsx
function App(props: { id: number }) {
const handleClick = useCallback(
//⬇️ add type here
(message: string) => {
console.log("name");
},
[props.id]
);
return (
{message}
);
}
```
## useRef
### Basic useRef
```tsx
export const Component = () => {
// pass type if it doesn't have initial value
const id1 = useRef();
// no need to pass type if it have initial value
const id2 = useRef("");
useEffect(() => {
id1.current = "Random value!";
}, []);
return ;
};
```
### useRef with HTML element
you can hover over the type of ref in the element to check what it accepts in your editor
```tsx
export const Component1 = () => {
// add null to initial value
const ref = useRef(null);
//(property) React.ClassAttributes.ref?: React.LegacyRef | undefined
return ;
};
```
alternative using `ElementRef` type helper
```tsx
import { useRef, ElementRef } from "react";
import { OtherComponent } from "./other-component";
// the easy alternative, you can use ElementRef
const Component1 = () => {
const audioRef = useRef>(null);
return ;
}
// you can use ElementRef for component too
type OtherComponentRef = ElementRef;
const Component2 = () => {
const ref = useRef(null);
return Hello;
};
```
### useRef with forwardRef
```tsx
type InputProps = {
className: string;
};
const MyInput = React.forwardRef((props, ref) => {
return ;
});
// add displayName if you are using function expression, so its has a name in React Devtool
MyInput.displayName = "MyInput";
function App() {
const input = React.useRef(null);
useEffect(() => {
// focus to input element on first render
if (input.current) {
input.current.focus();
}
}, []);
return ;
}
```
### Making a Read-Only Ref Mutable
```tsx
export const Component = () => {
const ref1 = useRef(null);
// if you pass null to initial value
// this not allowed to change directly
ref1.current = "Hello";
const ref2 = useRef();
// if initial value is undefined this is allowed to change (mutable)
ref2.current = "Hello";
return null;
};
```
## useReducer
You can use [Discriminated Unions](https://www.totaltypescript.com/discriminated-unions-are-a-devs-best-friend) for reducer actions. Don't forget to define the return type of reducer, otherwise TypeScript will infer it.
```tsx
import { useReducer } from "react";
const initialState = { count: 0 };
type ACTIONTYPE =
| { type: "increment"; payload: number }
| { type: "decrement"; payload: string };
function reducer(state: typeof initialState, action: ACTIONTYPE) {
switch (action.type) {
case "increment":
return { count: state.count + action.payload };
case "decrement":
return { count: state.count - Number(action.payload) };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
>
);
}
```
## Context
```tsx
import { createContext, useState } from "react";
type ThemeContextType = "light" | "dark";
const ThemeContext = createContext(null);
//if you have proper default value, you dont need specify null
//const ThemeContext = createContext("light");
const useTheme = () => {
const theme = useContext(ThemeContext);
if (!theme) {
throw new Error(
"useTheme has to be used within "
);
}
return theme;
};
const App = () => {
const [theme, setTheme] = useState("light");
return (
);
};
const SomeComponent = () => {
// since the value has been checked inside useTheme, no need checking null value
const theme = useTheme();
return
current theme: {theme}.
;
};
```
## Polymorphic
Imagine a `Button` component that renders a `