[th][DONE] Basic React #1 V.1

󰃭 2024-10-12

Slide

สิ่งที่จะได้เรียนรู้ & Table of Contents

  • สร้างโปรเจค React ด้วย Vite
  • Create React Component
    • Ex. Create Input Text Component
    • Ex. Create Nested Component (Greeting Component , Input Text Component + Button)
  • React JSX
    • Display Primative Data
    • Display Object
    • Function in JSX
    • List in JSX
    • React Component Defination (Advanced)
    • Prepare for Next Create Search, List Components
  • Handler Funciton in JSX
  • React Props
  • React State & Hooks
    • useState
    • useEffect
  • What’s Next?

เตรียมความพร้อมก่อนเริ่มเขียน React

ติดตั้ง NodeJS and NPM เปลี่ยนมาใช้ pnpm

ตรวจสอบการติดตั้ง Node

node -v

ตรวจสอบการติดตั้ง NPM

npm -v

ติดตั้ง pnpm แทน NPM

npm install -g pnpm

ตรวจสอบการติดตั้ง pnpm

pnpm -v

สร้างโปรเจค React ด้วย Vite

Vite เว็บไซต์: https://vitejs.dev/

Vite อ่านว่า “วีด (veet)” เป็นคำจากภาษาฝรั่งเศสที่แปลว่า “เร็ว”

key features ของ Vite
- สร้างโปรเจคเร็ว
- รีเฟรชเร็ว
- สามารถใช้งานได้กับ React, Vue, Svelte และ TypeScript
- ไม่ต้อง bundle ไฟล์ใหญ่ๆ ในการพัฒนา
- สามารถใช้งานได้กับ ES Modules และ CSS Modules
# example command
pnpm create vite <app-name> --template <react-ts, react, vue, vue-ts, svelte, svelte-ts>

เริ่มต้นสร้าง Project react ด้วย Vite

pnpm create vite todo-app --template react-ts

หลังจากติดตั้งเสร็จ จะแสดงข้อความดังนี้

cd todo-app
pnpm install
pnpm run dev

หลังจากนั้นเปิด Browser แล้วเข้าไปที่ URL: http://localhost:5173 จะเห็นหน้าเว็บที่สร้างขึ้นมา

Walkthrough โครงสร้างโปรเจค React ที่สร้างขึ้นมา

package.json หลังจากติดตั้ง และรันคำสั่งตาม Tutorial ด้านบนพบว่ามีคำสั่ง pnpm run <scripts> ซึ่งหมายถึงการ server

{
  /*code snipped*/
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  }
  /*code snipped*/
}

index.html สังเกตุว่าจะมีการสร้าง สำหรับใส่ React Component

<body>
  <div id="root"></div>
  <script type="module" src="/src/main.tsx"></script>
</body>

src/main.tsx สำหรับ render React Component ที่เขียนด้วย TypeScript

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

src/App.tsx

import React from "react";

const App = () => {
  return <h1>Hello World</h1>;
};

export default App;

ลองแก้ไข src/App.tsx และดูผลลัพธ์ที่ Browser

src/App.tsx

import React from "react";

const App = () => {
  return <h1>สวัสดี,hi there.</h1>;
};

export default App;

Take a break, 10 minutes

#1 เริ่มสร้าง React Component

Rules

  • ใช้ชื่อไฟล์เป็น PascalCase
  • ตัวอักษรแรกของชื่อ Component ให้เป็นตัวใหญ่

Simple Component

สร้าง Component InputText จาก App.tsx

import React from "react";

const InputText = () => {
  return (
    <div>
      <label htmlFor="input-text">Input Text</label>
      <input type="text" id="input-text" />
    </div>
  );
};

App.tsx แก้ไขเพื่อเรียกใช้ Component InputText

import React from "react";
import InputText from "./InputText";

const App = () => {
  return (
    <div>
      <h1>สวัสดี,hi there.</h1>
      <InputText />
    </div>
  );
};

Nested Component

สร้าง Component Greeting และเรียกใช้ Component InputText ใน Greeting

import React from "react";
import InputText from "./InputText";

const Greeting = () => {
  return (
    <div>
      <h2>Greeting</h2>
      <InputText />
      <button>Submit</button>
    </div>
  );
};

App.tsx แก้ไขเพื่อเรียกใช้ Component Greeting

import React from "react";
import Greeting from "./Greeting";

const App = () => {
  return (
    <div>
      <h1>สวัสดี,hi there.</h1>
      <Greeting />
    </div>
  );
};

#2 React JSX

  • JSX คือ JavaScript XML
  • JSX ใช้เขียน Component ใน React

App.tsx แก้ไขเพื่อใช้ทดสอบการใช้งาน JSX Ex. display primative data เช่น string, number

import React from "react";

const name = "foo";

const App = () => {
  return <div>Hi, {name}</div>;
};

App.tsx Ex. display object

import React from "react";

const user = {
  name: "foo",
  age: 28,
};

const App = () => {
  return (
    <div>
      Hi, {user.name} <br />
      I'm {user.age} years old.
    </div>
  );
};

App.tsx Ex. funciton in JSX calculate age

import "./App.css";

const user = {
  name: "foo",
  birth_year: 1996,
};

function calculateAge(birthYear: number) {
  const currentYear = new Date().getFullYear();
  return currentYear - birthYear;
}

function App() {
  return (
    <>
      Hi, {user.name}, I'm {calculateAge(user.birth_year)} years old.
    </>
  );
}

export default App;

App.tsx Ex. list in JSX

import React from "react";

const users = [
  { name: "foo", age: 28 },
  { name: "bar", age: 30 },
  { name: "baz", age: 25 },
];

const App = () => {
  return (
    <div>
      <h1>Users</h1>
      <ul>
        {users.map((user, index) => (
          <li key={index}>
            {user.name} - {user.age}
          </li>
        ))}
      </ul>
    </div>
  );
};

React Component Defination (Advanced)


// function declaration
function () { ... }

// arrow function declaration
const () => { ... }

// allowed
const item => { ... }

// allowed (recommemded)
const (item) => { ... }

// not allowed
const item, index = { ... }

// allowed (recommemded)
const (item, index) => { ... }

Prepare for Next Create Search, List Components

src/components/Search.tsx สร้าง Component Search

import React from "react";

const Search = () => {
  return (
    <div>
      <label htmlFor="search">Search: </label>
      <input id="search" type="text" />
    </div>
  );
};

export default Search;

src/components/List.tsx สร้าง Component List

import React from "react";

const items = [
  {
    title: "React",
    url: "https://reactjs.org/",
    author: "Jordan Walke",
    num_comments: 3,
    points: 4,
    objectID: 0,
  },
  {
    title: "Redux",
    url: "https://redux.js.org/",
    author: "Dan Albramov, Andrew Clark",
    num_comments: 2,
    points: 5,
    objectID: 1,
  },
];

const List = () => {
  return (
    <div>
      <ul>
        {items.map((item) => (
          <li key={item.objectID}>
            <span>
              <a href={item.url}>{item.title}</a>
            </span>
            <span>{item.author}</span>
            <span>
              {" "}
              comment: {item.num_comments}, point: {item.points}
            </span>
          </li>
        ))}
      </ul>
    </div>
  );
};

src/App.tsx แก้ไขเพื่อเรียกใช้ Component Search และ List

import React from "react";

import Search from "./components/Search";
import List from "./components/List";

const App = () => {
  return (
    <div>
      <h1>สวัสดี,hi there.</h1>

      <Search />
      <hr />
      <List />
    </div>
  );
};

Take a break, 10 minutes

#3 Handler Funciton in JSX

Search.tsx แก้ไขเพื่อเพิ่ม Event Handler ใน input

import React from "react";

const Search = () => {
  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    console.log(event.target.value);
  };

  return (
    <div>
      <label htmlFor="search">Search: </label>
      <input id="search" type="text" onChange={handleChange} />
    </div>
  );
};

Example

// don't do this
<>
  <input id="search" type="text" onChange={handleChange()} />
  // do this
  <input id="search" type="text" onChange={handleChange} />
  // do this
  <input id="search" type="text" onChange={(event) => handleChange(event)} />
</>

#4 React Props

React Props คืออะไร?

  • Props ย่อมาจาก Properties
  • ใช้สำหรับส่งข้อมูลจาก Parent Component ไปยัง Child Component
  • ใช้สำหรับส่ง Function จาก Parent Component ไปยัง Child Component

จากตัวอย่างการทำ Example ที่ผ่านมาเราประกาศ Items List ไว้ด้านบนของ Scope ทีนี้ให้ทำการย้ายไปไว้ใน App Component และส่งผ่าน Props ไปยัง List Component

App.tsx แก้ไขเพื่อส่ง Props ไปยัง List Component

const App = () => {
  const items = [
    {
      title: "React",
      url: "https://reactjs.org/",
      author: "Jordan Walke",
      num_comments: 3,
      points: 4,
      objectID: 0,
    },
    {
      title: "Redux",
      url: "https://redux.js.org/",
      author: "Dan Albramov, Andrew Clark",
      num_comments: 2,
      points: 5,
      objectID: 1,
    },
  ];

  return (
    <div>
      <h1>สวัสดี,hi there.</h1>

      <Search />
      <hr />
      <List items={items} />
    </div>
  );
};

List.tsx แก้ไขเพื่อรับ Props และแสดงข้อมูล

import React from "react";

interface Item {
  title: string;
  url: string;
  author: string;
  num_comments: number;
  points: number;
  objectID: number;
}

interface ListProps {
  items: Item[];
}

const List = (props: ListProps) => {
  return (
    <div>
      <ul>
        {props.items.map((item) => (
          <li key={item.objectID}>
            <span>
              <a href={item.url}>{item.title}</a>
            </span>
            <span>{item.author}</span>
            <span>
              {" "}
              comment: {item.num_comments}, point: {item.points}
            </span>
          </li>
        ))}
      </ul>
    </div>
  );
};

สรุปเป็นการส่ง Props จาก Parent Component ไปยัง Child Component และใช้ Props ในการแสดงข้อมูลใน Child Component

เพิ่ม Component Item เพื่อรับค่า Props

import React from "react";

interface Item {
  title: string;
  url: string;
  author: string;
  num_comments: number;
  points: number;
  objectID: number;
}

const Item = (props: Item) => {
  return (
    <li key={props.objectID}>
      <span>
        <a href={props.url}>{props.title}</a>
      </span>
      <span>{props.author}</span>
      <span>
        {" "}
        comment: {props.num_comments}, point: {props.points}
      </span>
    </li>
  );
};

List.tsx แก้ไขเพื่อใช้ Component Item

import React from "react";
import Item from "./Item";

interface Item {
  title: string;
  url: string;
  author: string;
  num_comments: number;
  points: number;
  objectID: number;
}

interface ListProps {
  items: Item[];
}

const List = (props: ListProps) => {
  return (
    <div>
      <ul>
        {props.items.map((item) => (
          <Item {...item} />
        ))}
      </ul>
    </div>
  );
};

Take a break, 10 minutes

#5 React State & Hooks

React State คืออะไร?

  • State ใช้เก็บข้อมูลที่เปลี่ยนแปลงได้
  • ใช้ในการเก็บข้อมูลที่เปลี่ยนแปลงได้ใน Component
  • ใช้ในการเก็บข้อมูลที่เปลี่ยนแปลงได้ใน Component และส่ง Props ไปยัง Child Component

ลองเริ่มใช้งาน State จาก Search.tsx กันโดยโจทย์คือเมื่อพิมพ์ข้อความใน input แล้วจะแสดงข้อความนั้นออกมา

const Search = () => {
  let searchTerm = "Searching...";

  const handleChange = (event: any) => {
    searchTerm = event.target.value;
  };

  return (
    <div>
      <label htmlFor="search">Search: </label>
      <input id="search" type="text" onChange={handleChange} />

      <p>{searchTerm}</p>
    </div>
  );
};

export default Search;

useState

useState

useState is a React Hook that lets you add a state variable to your component.

const useState = (initialState: any) => {
  let state = initialState;

  const setState = (newState: any) => {
    state = newState;
  };

  return [state, setState];
};

const [count, setCount] = useState("Searching...");

ใช้ useState ใน Search.tsx

import React, { useState } from "react";

const Search = () => {
  const [searchTerm, setSearchTerm] = useState("Searching...");

  const handleChange = (event: any) => {
    setSearchTerm(event.target.value);
  };

  return (
    <div>
      <label htmlFor="search">Search: </label>
      <input id="search" type="text" onChange={handleChange} />

      <p>{searchTerm}</p>
    </div>
  );
};

Callback Handlers in JSX

ใช้ Callback Handlers ในการส่ง Function จาก Parent Component ไปยัง Child Component

สร้าง Function ใน App.tsx และส่ง Function ไปยัง Search Component

import React, { useState } from "react";

// A
const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
  console.log(event.target.value);
};

const App = () => {
  return (
    <div>
      <h1>สวัสดี,hi there.</h1>
      {/* B */}
      <Search onSearch={handleChange} />
      ...
    </div>
  );
};

สร้าง Props ใน Search.tsx และใช้ Callback Handlers

import React from "react";

interface SearchProps {
  onSearch: (event: React.ChangeEvent<HTMLInputElement>) => void;
}

const Search = (props: SearchProps) => {
  const [searchTerm, setSearchTerm] = useState("Searching...");

  const handleChange = (event: any) => {
    setSearchTerm(event.target.value);

    props.onSearch(event);
  };

  return (
    <div>
      <label htmlFor="search">Search: </label>
      <input id="search" type="text" onChange={handleChange} />

      <p>{searchTerm}</p>
    </div>
  );
};

Lifting State in React

Lifting State คือการย้าย State จาก Child Component ไปยัง Parent Component ให้ลองจินตนาการว่าเราต้องการที่จะ Search ข้อมูลจาก List ที่อยู่ใน List Component แล้วแสดงผลลัพธ์ที่ได้ใน App Component

App.tsx สร้าง Function ใน App.tsx และส่ง Function ไปยัง List Component

import React, { useState } from "react";

const App = () => {
  const [searchTerm, setSearchTerm] = useState("");

  const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
    console.log(event.target.value);
  };

  return (
    <div>
      <h1>สวัสดี,hi there.</h1>

      <Search onSearch={handleSearch} />
      <hr />
      <List items={items} />
    </div>
  );
};

Search.tsx แก้ไขเพื่อส่ง Props ไปยัง App Component

interface SearchProps {
  onSearch: (event: React.ChangeEvent<HTMLInputElement>) => void;
}

const Search = (props: SearchProps) => {
  return (
    <div>
      <label htmlFor="search">Search: </label>
      <input id="search" type="text" onChange={props.onSearch} />
    </div>
  );
};

export default Search;

App.tsx เพิ่มการกรองข้อมูล Items และส่งไปยัง List Component

import "./App.css";
import Search from "./components/Search";
import List from "./components/List";
import React, { useState } from "react";

const App = () => {
  const items = [
    {
      title: "React",
      url: "https://reactjs.org/",
      author: "Jordan Walke",
      num_comments: 3,
      points: 4,
      objectID: 0,
    },
    {
      title: "Redux",
      url: "https://reduxjs.org/",
      author: "Dan Albramov, Andrew Clark",
      num_comments: 2,
      points: 5,
      objectID: 1,
    },
  ];

  const [searchTerm, setSearchTerm] = useState("");

  const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
    setSearchTerm(event.target.value);
  };

  const searchedItems = items.filter((item) => {
    return item.title.includes(searchTerm) || item.author.includes(searchTerm);
  });

  return (
    <div>
      <h1>Hi</h1>
      <Search onSearch={handleSearch} />
      <hr />
      <List items={searchedItems} />
    </div>
  );
};
export default App;

Props Handling (Advanced)

Object Destructuring

const user = {
  name: "foo",
  age: 28,
};

// without object destructuring
const name = user.name;
const age = user.age;

// with object destructuring
const { name, age } = user;

const list = ["foo", "bar", "baz"];

// without array Destructuring
const first = list[0];
const second = list[1];
const third = list[2];

// with array destructuring
const [first, second, third] = list;

Search.tsx Ex.1

interface SearchProps {
  onSearch: (event: React.ChangeEvent<HTMLInputElement>) => void;
}

const Search = (props: SearchProps) => {
  const { onSearch } = props;
  return (
    <div>
      <label htmlFor="search">Search: </label>
      <input id="search" type="text" onChange={props.onSearch} />
    </div>
  );
};

export default Search;

Search.tsx Ex.2

interface SearchProps {
  onSearch: (event: React.ChangeEvent<HTMLInputElement>) => void;
}

const Search: FC<SearchProps> = ({ onSearch }) => {
  return (
    <div>
      <label htmlFor="search">Search: </label>
      <input id="search" type="text" onChange={props.onSearch} />
    </div>
  );
};

export default Search;

Nested Destructuring

const user = {
  name: "foo",
  age: 28,
  address: {
    city: "Bangkok",
    country: "Thailand",
  },
};

const {
  name,
  age,
  address: { city, country },
} = user;

useEffect

useEffect

useEffect เป็นส่วนหนึ่งใน React Hooks ที่ใช้ในการจัดการ Side Effect ใน Component ของเรา เช่น

  • การเรียก API
  • การจัดการ Event Listener
  • การจัดการ Subscription
  • การจัดการ Timer
  • อื่นๆ
import React, { useState, useEffect } from "react";

const App = () => {
  const [searchTerm, setSearchTerm] = useState("");

  useEffect(() => {
    console.log("searchTerm:", searchTerm);
  }, [searchTerm]);

  const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
    setSearchTerm(event.target.value);
  };

  return (
    <div>
      <h1>สวัสดี,hi there.</h1>

      <Search onSearch={handleSearch} />
      <hr />
      <List items={items} />
    </div>
  );
};

DONE 🎉


What’s Next?

  • Visual DOM vs Actual DOM
    • Virtual DOM
    • Reconciliation
    • Diffing Algorithm
    • Re-rendering
    • Performance
    • React Fiber
    • Concurrent Mode
    • Suspense
    • Error Boundary
  • React Component Lifecycle
  • Data Fetching in React
    • Axios, React Query (v5), state (isLoading, isError, data)
  • Hooks
    • useState
    • useEffect
    • useContext
    • useReducer
    • useCallback
    • useMemo
    • useRef
    • useImperative
    • useForwardRef
  • React Router
  • State Management
    • Redux
    • MobX
    • Zustand

NextJS