[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
- NodeJS เว็บไซต์: https://nodejs.org/en/
ตรวจสอบการติดตั้ง 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 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 เป็นส่วนหนึ่งใน 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