[
  {
    "path": "README.md",
    "content": "# Simple React Typescript Cheatsheet\n\n- [Simple React Typescript Cheatsheet](#simple-react-typescript-cheatsheet)\n  - [Typing props with inline type](#typing-props-with-inline-type)\n  - [Typing props with Type](#typing-props-with-type)\n  - [Typing props with default value](#typing-props-with-default-value)\n  - [Typing props with children](#typing-props-with-children)\n  - [Using Native HTML props to React Components](#using-native-html-props-to-react-components)\n    - [1. Basic](#1-basic)\n    - [2. Combine with your type](#2-combine-with-your-type)\n    - [3. Overriding Native Props](#3-overriding-native-props)\n    - [4. Extracting Props from Custom Components](#4-extracting-props-from-custom-components)\n  - [Typing Event Handlers from native element](#typing-event-handlers-from-native-element)\n  - [useState](#usestate)\n  - [useCallback](#usecallback)\n  - [useRef](#useref)\n    - [Basic useRef](#basic-useref)\n    - [useRef with HTML element](#useref-with-html-element)\n    - [useRef with forwardRef](#useref-with-forwardref)\n    - [Making a Read-Only Ref Mutable](#making-a-read-only-ref-mutable)\n  - [useReducer](#usereducer)\n  - [Context](#context)\n  - [Polymorphic](#polymorphic)\n  - [Types or Interfaces?](#types-or-interfaces)\n  - [Resources](#resources)\n\n\n## Typing props with inline type\n\n```tsx\nfunction Button(props: { children: React.ReactNode }) {\n  return <button>{props.children}</button>;\n}\n```\n\n## Typing props with Type\n\n```tsx\n// you can use interface too\ntype ButtonProps = {\n  className: string;\n  children: React.ReactNode;\n};\n\nfunction Button(props: ButtonProps) {\n  return <button className={props.className}>{props.children}</button>;\n}\n\n// with destructuring\nfunction OtherButton({ className, ...props }: ButtonProps) {\n  return <button className={className}>{props.children}</button>;\n}\n```\n\n## Typing props with default value\n\n```tsx\ntype ButtonProps = {\n  disabled?: boolean;\n  className: string;\n  children: React.ReactNode;\n};\n\nfunction Button({ disabled = true, ...props }: ButtonProps) {\n  return (\n    <button disabled={disabled} {...props}>\n      {props.children}\n    </button>\n  );\n}\n```\n\n## Typing props with children\n\n```tsx\ntype ButtonProps = {\n  // accept everything React can render\n  children: React.ReactNode;\n};\n\nfunction Button(props: ButtonProps) {\n  return <button>{props.children}</button>;\n}\n```\n\n## Using Native HTML props to React Components\n\n### 1. Basic\n\n```tsx\nimport React, { ComponentProps } from \"react\";\n\n//ComponentProps<\"button\"> : get all type/props from native button element\n\nfunction Button(props: ComponentProps<\"button\">) {\n  return <button>{props.children}</button>;\n}\n```\n\n### 2. Combine with your type\n\n```tsx\nimport React, { ComponentProps } from \"react\";\n\ntype ButtonProps = ComponentProps<\"button\"> & {\n  variant: \"primary\" | \"secondary\";\n};\n\nfunction Button(props: ButtonProps) {\n  return <button {...props}>{props.children}</button>;\n}\n```\n\n### 3. Overriding Native Props\n\n```tsx\nimport React, { ComponentProps } from \"react\";\n\n//remove onChange property from input with Omit<Type, Keys> and combine with new type\ntype InputProps = Omit<ComponentProps<\"input\">, \"onChange\"> & {\n  onChange: (value: string) => void;\n};\n\nfunction Input(props: InputProps) {\n  return <input {...props} />;\n}\n```\n\n### 4. Extracting Props from Custom Components\n\nUseful when author of some external library dont export the type definition\n\n```tsx\nimport { ComponentProps } from \"react\";\nimport { Navbar } from \"some-ui-library\";\n\ntype NavBarProps = ComponentProps<typeof NavBar>;\n```\n\n## Typing Event Handlers from native element\n\nHover native html props in VSCode, you can copy paste the type definition\n\n```tsx\ntype ButtonProps = {\n  other?: Boolean;\n  onClick?: React.MouseEventHandler<HTMLButtonElement>;\n};\n```\n\n![event handler](event-handler.png \"event handler\")\n\n## useState\n\n```tsx\n// ❌ Typescript already know `text` type is string\nconst [text, setText] = useState<string>(\"\");\n// ✅ no need to tell typescript, only work with primitive value\nconst [text, setText] = useState(\"\");\n```\n\n```tsx\ntype Tag = {\n  id: number;\n  value: string;\n};\n\nconst [tags, setTags] = useState<Tag[]>([]);\n```\n\n```tsx\n// data : Data | undefined\nconst [data, setData] = useState<Data>();\n// data : Data | undefined\nconst [data, setData] = useState<Data>(undefined);\n// data : Data | null\nconst [data, setData] = useState<Data | null>(null);\n```\n\n## useCallback\n\n```tsx\nfunction App(props: { id: number }) {\n  const handleClick = useCallback(\n    //⬇️ add type here\n    (message: string) => {\n      console.log(\"name\");\n    },\n    [props.id]\n  );\n\n  return (\n    <div>\n      <p>{message}</p>\n      <button onClick={() => handleClick(\"hello\")}>button 1</button>\n      <button onClick={() => handleClick(\"hello\")}>button 2</button>\n    </div>\n  );\n}\n```\n\n## useRef\n\n### Basic useRef\n\n```tsx\nexport const Component = () => {\n  // pass type if it doesn't have initial value\n  const id1 = useRef<string>();\n  // no need to pass type if it have initial value\n  const id2 = useRef(\"\");\n\n  useEffect(() => {\n    id1.current = \"Random value!\";\n  }, []);\n\n  return <div></div>;\n};\n```\n\n### useRef with HTML element\n\nyou can hover over the type of ref in the element to check what it accepts in your editor\n\n```tsx\nexport const Component1 = () => {\n  // add null to initial value\n  const ref = useRef<HTMLAudioElement>(null);\n\n  //(property) React.ClassAttributes<HTMLAudioElement>.ref?: React.LegacyRef<HTMLAudioElement> | undefined\n  return <audio ref={ref} />;\n};\n\n```\n\nalternative using `ElementRef` type helper\n\n```tsx\nimport { useRef, ElementRef } from \"react\";\nimport { OtherComponent } from \"./other-component\";\n\n// the easy alternative, you can use ElementRef\nconst Component1 = () => {\n  const audioRef = useRef<ElementRef<\"audio\">>(null);\n  return <audio ref={audioRef}>Hello</audio>;\n}\n\n// you can use ElementRef for component too\ntype OtherComponentRef = ElementRef<typeof OtherComponent>;\n\nconst Component2 = () => {\n  const ref = useRef<OtherComponentRef>(null);\n \n return <OtherComponent ref={ref}>Hello</OtherComponent>;\n};\n\n```\n\n### useRef with forwardRef\n\n```tsx\ntype InputProps = {\n  className: string;\n};\n\nconst MyInput = React.forwardRef<HTMLInputElement, InputProps>((props, ref) => {\n  return <input ref={ref} className={props.className} />;\n});\n// add displayName if you are using function expression, so its has a name in React Devtool\nMyInput.displayName = \"MyInput\";\n\nfunction App() {\n  const input = React.useRef<HTMLInputElement>(null);\n\n  useEffect(() => {\n    // focus to input element on first render\n    if (input.current) {\n      input.current.focus();\n    }\n  }, []);\n\n  return <MyInput className=\"input-style\" ref={input} />;\n}\n```\n\n### Making a Read-Only Ref Mutable\n\n```tsx\nexport const Component = () => {\n  const ref1 = useRef<string>(null);\n  // if you pass null to initial value\n  // this not allowed to change directly\n  ref1.current = \"Hello\";\n\n  const ref2 = useRef<string>();\n  // if initial value is undefined this is allowed to change (mutable)\n  ref2.current = \"Hello\";\n\n  return null;\n};\n```\n\n## useReducer\n\nYou 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.\n\n```tsx\nimport { useReducer } from \"react\";\n\nconst initialState = { count: 0 };\n\ntype ACTIONTYPE =\n  | { type: \"increment\"; payload: number }\n  | { type: \"decrement\"; payload: string };\n\nfunction reducer(state: typeof initialState, action: ACTIONTYPE) {\n  switch (action.type) {\n    case \"increment\":\n      return { count: state.count + action.payload };\n    case \"decrement\":\n      return { count: state.count - Number(action.payload) };\n    default:\n      throw new Error();\n  }\n}\n\nfunction Counter() {\n  const [state, dispatch] = useReducer(reducer, initialState);\n  return (\n    <>\n      Count: {state.count}\n      <button onClick={() => dispatch({ type: \"decrement\", payload: \"5\" })}>\n        -\n      </button>\n      <button onClick={() => dispatch({ type: \"increment\", payload: 5 })}>\n        +\n      </button>\n    </>\n  );\n}\n```\n\n## Context\n\n```tsx\nimport { createContext, useState } from \"react\";\n\ntype ThemeContextType = \"light\" | \"dark\";\n\nconst ThemeContext = createContext<ThemeContextType | null>(null);\n\n//if you have proper default value, you dont need specify null\n//const ThemeContext = createContext<ThemeContextType>(\"light\");\n\nconst useTheme = () => {\n  const theme = useContext(ThemeContext);\n\n  if (!theme) {\n    throw new Error(\n      \"useTheme has to be used within <ThemeContext.Provider>\"\n    );\n  }\n  return theme;\n};\n\nconst App = () => {\n  const [theme, setTheme] = useState<ThemeContextType>(\"light\");\n\n  return (\n    <ThemeContext.Provider value={theme}>\n      <MyComponent />\n    </ThemeContext.Provider>\n  );\n};\n\n\nconst SomeComponent = () => {\n   // since the value has been checked inside useTheme, no need checking null value\n   const theme = useTheme();\n   return <p>current theme: {theme}.</p>;\n};\n```\n\n## Polymorphic\n\nImagine a `Button` component that renders a `<button>` element, but with your fancy button styles. If want to render the Button component as an a other element we might have an API like:\n\n```tsx\n<Button variantColor=\"primary\" href=\"https://blog.makerx.com.au/\" as=\"a\">\n  Click me\n</Button>\n```\n\nThis looks nice, but its not work realy well with typescript. here the alternative using `radix-ui/react-slot`\n\n```tsx\n\n// Button.tsx\nimport { Slot } from '@radix-ui/react-slot'\n\ntype ButtonProps = React.ComponentPropsWithoutRef<'button'> & {\n  variantColor: 'primary' | 'secondary' | 'danger'\n  asChild?: boolean\n}\n\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>((props, forwardedRef) => {\n  const { variantColor, asChild, ...buttonProps } = props\n  const Component = (asChild ? Slot : 'button') as 'button'\n\n  return (\n    <Component\n      {...buttonProps}\n      ref={forwardedRef}\n      className={clsx(\n          // ...\n      )}\n    />\n  )\n}\n\n// App.tsx\n\nfunction App() {\n  return (\n    <div>\n       {/* asChild must be true */}\n      <Button variantColor=\"primary\" asChild>\n        {/* render button component as link */}\n        <a href=\"https://google.com\">About</a>\n      </Button>\n    </div>\n  )\n}\n```\n\n## Types or Interfaces?\n\n`interface`s are different from `type`s in TypeScript, but they can be used for very similar things as far as common React uses cases are concerned. Here's a helpful rule of thumb:\n\n- Always use `interface` for public API's definition when authoring a library or 3rd party ambient type definitions.\n\n- Consider using `type` for your React Component Props and State, because it is more constrained.\n\nTypes are useful for union types (e.g. `type MyType = TypeA | TypeB`) whereas Interfaces are better for declaring dictionary shapes and then `implementing` or `extending` them.\n\n## Resources\n\n- [React Total Typescript](https://www.totaltypescript.com/tutorials/react-with-typescript)\n- [Discriminated Unions are a Frontend Dev's Best Friend](https://www.totaltypescript.com/discriminated-unions-are-a-devs-best-friend)\n- [Other React TypeScript Cheatsheets\n  ](https://react-typescript-cheatsheet.netlify.app/)\n"
  }
]