⚛️ React+TypeScript 101 🥡
Tags: react, typescript
Published: 2021-02-25 08:31:33

## บันทึกความเข้าใจจาก

[React ไปวันๆ EP.4 - Typescript in React 101](https://youtu.be/OlcaCks-GlE)

{% github https://github.com/React-in-Thai/react-ts-101 %}

## Topic

* [UseState](#UseState)
* [Promise](#Promise)
* [Chidren Types](#Chidren Types)
* [Compound Component](#Compound component)
* [Bye Bye Enum](#Bye Bye Enum)
* [React & html type that you should know](#React & html type that you should know)
* [CSS type](#CSS type)

## Why TypeScript

เมื่อก่อน JavaScript ถูกก design ให้ใช้งานง่ายๆสำหรับ brower แต่ตอนนี้มันอยู่ทุกส่วนแล้ว

---

## Type

เหมือน interface แต่สามารถกำหนดชื่อให้ primitives type ได้ ใช้ในรูป unions ได้ ในรูป tuples ได้

> Tuple คืออะไร
> Tuple เป็น Data Structure แบบหนึ่งที่เป็น Array แบบ Fixed Size โดยที่ Data ที่อยู่ภายใน Tuple ไม่จำเป็นต้องเป็น Type ชนิดเดียวกัน

> unions คืออะไร ดูข้างล่างนี้

```ts
type User = {
  firstname: string;
  lastname: string;
};
type NumberOrStringOrStudent = string | number | User;

let myValue: NumberOrStringOrStudent;
const sampleUser: User = {
  firstname: "Sippakorn",
  lastname: "Raksakiart",
};
```

```js
// ประกาศ Tuple
let option: [string, boolean, number];

option = ["uppercase", true, 1]; // OK
option2 = [false, 2, "lowercase"]; // ERROR
```

---

## UseState <a name="UseState"></a>

```jsx
export type ProfileProps = {
  name: string,
  age: number,
};
// todo: create Prop type for this component, also optional type
export function BasicProps({ name, age }: ProfileProps) {
  return (
    <div>
      <div>Name: {name}</div>
      <div>Age: {age}</div>
    </div>
  );
}
```

ถ้าอยากได้แบบ optional ละก็เพิ่ม `?`

```jsx
export type ProfileProps = {
  name?: string,
  age?: number,
};
```

```tsx
const [boolOrNum, setBoolOrNum] = useState();
const [boolOrNum, setBoolOrNum] = useState<boolean | number>(); // need to be boolean | number

<button
  onClick={() =>
    setBoolOrNum((c) => {
      if (typeof c === "number") {
        return c + 1;
      }
    })
  }
>
  Increment
</button>;
```

อย่าลืมที่จะใส่ Item[]

```jsx

interface Item {
  id: string
  name: string
}

export function Example2() {
  const [data, setData] = useState<Item[]>([]);

  return data.map((item, i) => (
    <div>
      Item {i} is {item.id}
    </div>
  ));
}
```

## Promise <a name="Promise"></a>

ดูนาทีที่ 45.00

```tsx
import React, { useEffect, useState } from "react";
import { getData } from "../js/api";

const api = (id: string) => new Promise<string>((resolve, reject) => {});

// todo : create custom hook that set response from api to state and return the state
function useData(id: number) {
  const [data, setData] = useState<number>();
  useEffect(() => {
    (async function () {
      setData(await getData(id));
    })();
  }, [id]);
  return data;
}

// what if api dont have type?
```

จากข้างบนเราจะไม่รู้ว่า getData เป็น type อะไร เราอาจจะใส่ type ให้มันด้วยก็ได้
หรือไม่ก็ได้เพราะ reusult ออกไปก็ถูกต้องอยู่ดี

## Chidren types <a name="Chidren types"></a>

เข้าไปดู `React.ReactNode`

```tsx
type ReactChild = ReactElement | ReactText;

type ReactNode =
  | ReactChild
  | ReactFragment
  | ReactPortal
  | boolean
  | null
  | undefined;

interface ReactElement<
  P = any,
  T extends string | JSXElementConstructor<any> =
    | string
    | JSXElementConstructor<any>
> {
  type: T;
  props: P;
  key: Key | null;
}
```

เข้าไปดู `PropsWithChildren` (นาที52)

```tsx
type PropsWithChildren<P> = P & { children?: ReactNode };
```

หรือเราจะเขียน render prop

```tsx
import React from "react";

type Props = {
  header: React.ReactNode; // can react  Children
  sidebar: React.ReactElement; // jsx
  footer: string;
  render: (value: boolean) => React.ReactNode;
};

export function Example({
  header,
  sidebar,
  footer,
  children,
}: React.PropsWithChildren<Props>) {
  const [state, setState] = useState(false);
  return (
    <div>
      <header>{header}</header>
      <div>{sidebar}</div>
      <div>{children}</div>
      <footer>{footer}</footer>
      {render(state)}
    </div>
  );
}
```

## Compound component <a name="Compound component"></a>

มาดูกันว่าแบบนี้จะเขียน type ได้อย่างไร

```tsx
// Grid.tsx
import React from "react";

// todo : create Grid component that contain Row & Column

// outside
function App() {
  return (
    <Grid.Row>
      <Grid.Column>
        <div>Content 1</div>
      </Grid.Column>
      <Grid.Column>
        <div>Content 2</div>
      </Grid.Column>
    </Grid.Row>
  );
}
```

เพิ่มอันนี้เข้าไป

```tsx
const Grid = ({ children }: React.PropsWithChildren<{}>) => {
  return <div>{children}</div>;
};

const Row = ({ children }: React.PropsWithChildren<{}>) => {
  return <div>{children}</div>;
};

const Column = ({ children }: React.PropsWithChildren<{}>) => {
  return <div>{children}</div>;
};

// อย่าลืมที่ขะบอกว่า Row อยู่ใน Grid
Grid.Row = Row;
Grid.Column = Column;
```

หรือจะทำแบบนี้แทนก็ได้ (นาทที่1hr.02 ผมเองก็ยังไม่เข้าใจจุดนี้)

```tsx
interface Grid {
  ({ children }: React.PropsWithChildren<{}>): React.ReactNode;
  Row: typeof Row;
  Column: typeof Column;
}

const Grid: Grid = () => {
  return <div />;
};
```

## Bye Bye Enum  <a name="Bye Bye Enum"></a>

นาทีที่ 1hr.04

```tsx
import React from "react";

enum Colors {
  Red = "red",
  Blue = "blue",
  Green = "green",
  White = "white",
}

type Props = {
  color: Colors;
};
```

นอกจากนี้ท่าที่ทำไม่ได้

```tsx
cons c1:Color = 'red'
//ต้องใช้ท่านี้
const c1 = 'red' as Colors

```

หรือจะทำเป็น Object

```tsx
const Colors = {
 Red: 'red'
 Blue: 'blue'
}

type Prop ={
 color:Colors   // มันจะ error บอกว่าเอา Object มาทำ Type ไม่ได้
}

```

ต้องแปลง Object เป็น type

```tsx
const Colors = {
 Red: 'red'
 Blue: 'blue'
}
//typeof Colors = (typeof Colors) // ได้ struct Colors ละ
typeof Colors = (typeof Colors)[keyof typeof Colors] //return key

```

code จะใช้ได้เหมือน enum

```tsx
const Colors = {
  Red: "red",
  Blue: "blue",
} as const;

type Colors = typeof Colors[keyof typeof Colors];

type Props = {
  color: Colors;
};
```

เราสามารถทำง่ายกว่านี้โดยใช้ `enum`

```tsx
const Colors = {
  Red: "red",
  Blue: "blue",
} as const;
type Enum<T> = T[keyof T];
type Colors = Enum<typeof Colors>;

type Props = {
  color: Colors;
};
```

ต่อจากนี้จะเป็น part 2  
[⚛️ React ไปวันๆ EP.5 - Typescript in React 101 part 2](https://youtu.be/1WEmiCgZu64)
## React & html type that you should know 

ลองดูตัวอย่างต่อไปนี้ที่จำทำให้ cursor ไป focus ที่ input

```tsx
import React, { useRef, useEffect } from "react";

// todo : create input that will be focused if autoFocus prop is true
function Example({ autoFocus }: { autoFocus?: boolean }) {
  const inputRef = useRef();
  useEffect(() => {
    inputRef.current.focus();
  }, []);
  return (
    <form>
      <label htmlFor="input">Label</label>
      <input id="input" ref={inputRef} />
    </form>
  );
}
```
แต่มันยังผิดอยู่ต้องเพิ่ม `HtmlInputElement` เพิ่ม optinal auto ให้ด้วย
```tsx
import React, { useRef, useEffect } from "react";

// todo : create input that will be focused if autoFocus prop is true
function Example({ autoFocus }: { autoFocus?: boolean }) {
  const inputRef = useRef<HTMLInputElement>(null);
  useEffect(() => {
    inputRef.current?.focus();
  }, [autoFocus ]);
  return (
    <form>
      <label htmlFor="input">Label</label>
      <input id="input" ref={inputRef} />
    </form>
  );
}
```


# ค่อยมาเขียนต่อเนื่องจากเนื้อหาเริ่มจะซับซ้อนขึ้น