สอนเขียนเว็บด้วย React + TypeScript ปี 2023 (Intro to React) ตอนที่ 1

React May 4, 2023

สวัสดีครับ คอร์สนี้เป็นคอร์สเรียน React + TypeScript ที่ผมลองทำขึ้นมา เป้าหมายคือ เป็นเนื้อหาเบื้องต้น พื้นฐานสำหรับคนที่ไม่มีพื้นฐาน React + TypeScript มาก่อนเลย ให้สามารถทำเว็บไซต์ และ Deploy เว็บของเราได้ เนื้อหา ประมาณ 2-3 ชั่วโมง (แบบ Video) ซึ่งถ้านั่งเรียนจริงๆ + ทำ Mini Project ในคอร์สด้วย ผมว่า ใช้เวลาอย่างต่ำๆ 10 ชั่วโมงครับ

รูปแบบของ Tutorial มี 2 แบบคือ

  1. Video - ดูผ่าน Youtube
  2. Blog - เป็นบทความ อธิบายเพิ่มเติม

ทั้งสองรูปแบบใช้เนื้อหาเดียวกัน ข้อดีคือ สามารถเลือกเรียนจากรูปแบบที่ชอบได้ หรือเป็นแบบ Hybrid ก็ได้ เช่น ถ้าไม่เข้าใจเลย ก็อาจจะเริ่มจาก Video และทำตาม หรือถ้าพอมีพื้นฐาน ไม่เข้าใจแค่บางส่วน ก็อ่านบล็อกในเนื้อหาส่วนนั้นๆ เพิ่มเติมได้

Let's Get Things Started in 2021!
Photo by Clemens van Lay / Unsplash

0. เตรียมความพร้อม

ก่อนเริ่มเลยเนี่ย อยากให้ลองสำรวจดูความพร้อมของเรา รวมถึงเครื่องมือต่างๆ อาจจะไม่จำเป็นต้องเป๊ะๆ 100% นะครับ เป็นแค่แนวทาง อย่าง Editor หรือ Extensions ก็เป็นส่วนเสริมเท่านั้น จะลง จะใช้ ก็แล้วแต่เราเลย

มาดูสิ่งที่ต้องมี หรือต้องใช้มีอะไรบ้าง?

  • VS Code - ตัว Text Editor สำหรับเขียนโค๊ด (หรือใช้​ IDE/Editor ที่ชอบก็ได้)
  • Node.js / NPM - ติดตั้ง Node.js และ NPM เพื่อให้สามารถติดตั้ง Library ต่างๆได้
  • Terminal - มี Command Line Tool เช่น Terminal, iTerm2, Power Shell หรือสามารถใช้ built-in Terminal ที่มากับ VS Code ได้
  • Prettier - เพื่อเอาไว้ format code ของเราให้เป็นแนวทางเดียวกัน (สามารถใช้ Prettier Extension VS Code ได้)
  • มีความเข้าใจ HTML/CSS/JS เบื้องต้น หรือเคยเขียนเว็บไซต์มาก่อน
HTML คือ? สอนเขียน HTML สำหรับมือใหม่แบบละเอียด - Designil
มาอ่านบทความสอน HTML กันตั้งแต่พื้นฐานสำหรับมือใหม่กันครับ ละเอียดมากๆ พร้อมแนะนำคอร์สเรียนสำหรับฝึกฝนฝีมือเพียบ
บทความสอน HTML เบื้องต้น ที่ผมคิดว่าเขียนไว้ค่อนข้างดีเลยครับ

1. สร้างโปรเจ็ค

เราจะใช้ Vite ในการสร้างโปรเจ็ค React ขึ้นมา ซึ่ง Vite เป็น Frontend Tooling ที่ช่วยเรื่อง Config ต่างๆ ทำให้เราไม่ต้องเสียเวลาตรงนี้ เอาเวลามาโฟกัสการพัฒนาโค๊ดดีกว่า

วิธีการสร้างโปรเจ็ค React ด้วย Vite
บทความแนะนำ Frontend Tooling ตัวที่ชื่อว่า Vite (อ่านว่า วีด) ซึ่งเจ้า Vite เค้านิยามตัวเองว่าเป็น Next Generation Frontend Tooling ViteNext Generation Frontend ToolingGet Started TLDR; แบบ Prompt: npm create vite@latest # หรือ Yarnyarn create vite@latest แบบใช้ Options npm

เริ่มต้นสร้างโปรเจ็คด้วยคำสั่ง

npm create vite@latest hello-react -- --template react-ts


# หรือ เลือกจาก propmt
npm create vite@latest

เมื่อสร้างโปรเจ็คเสร็จเรียบร้อยนะครับ ต่อมาเราดูโครงสร้างภายในโปรเจ็คกันดีกว่า ว่ามีอะไรบ้าง

# tree -L 2 -I node_modules

├── index.html
├── package.json
├── public
│   └── vite.svg
├── src
│   ├── App.css
│   ├── App.tsx
│   ├── assets
│   ├── index.css
│   ├── main.tsx
│   └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

3 directories, 11 files
  • src - เป็น folder หลักที่เราจะเขียน React กันที่ folder นี้
  • index.html - เป็นไฟล์ html หลักของเรา
  • package.json - เป็นไฟล์ที่บอกว่าโปรเจ็คเรา ชื่ออะไร มี dependecies อะไรบ้าง
  • tsconfig.json - เป็น TypeScript config (เรายังไม่ต้องสนใจ ใช้เป็น default ก่อนครับ)

เปิดโปรเจ็คขึ้นมา ทำการสำรวจ ไฟล์และโฟลเดอร์ จากนั้นลอง start dev server ครับ

cd hello-react

# เปิดด้วย vs code 
code .

# ติดตั้ง dependencies
npm install

เมื่อติดตั้ง dependencies เรียบร้อย ก็ลอง Start dev server

npm run dev

ทำการเปิดหน้าเว็บ http://localhost:5173 เราจะเห็น React ของเรา หน้าตาแบบนี้

ทดลองแก้ไขไฟล์ src/App.tsx กดเซฟ และสังเกตหน้าเว็บ มีการเปลี่ยนแปลง โดยที่เราไม่ต้อง refresh หน้าเว็บเลย

Auto save และ Auto formatting

สำหรับคนที่ต้องการ Auto Save และ Auto Formatting โค๊ดให้เรา (บางคนชอบแก้โค๊ดแล้วลืมเซฟ) เราสามารถตั้งค่าตามนี้ได้ครับ

  • Prettier - เป็น Extensions หลัก ที่ควรมีติดไว้ (ไม่ใช้ Extension ก็ขอให้มีไฟล์ .prettierrc
  • ES Lint - เป็นตัวช่วยตรวจสอบโค๊ดของเรา (Optional)

มาดูที่การตั้งค่า ทั้ง 3 ค่า ที่แนะนำ คือ

  • การตั้งค่า Auto Save : Settings → หาคำว่า Auto Save เลือก onFocusChange
  • การตั้งค่า Auto Formatting : Settings → ติ๊กถูก Format on Save
  • การเลือก Default Formatting : Settings → ช่อง Default Formatter เลือกเป็น Prettier - Code formatter

ไฟล์ JSON Settings

{
  "files.autoSave": "onFocusChange",
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true
}

2. Basic React

React คือ JavaScript Library ที่ใช้สำหรับ ทำ User Interfaces (UI) เป็นโอเพ่นซอร์สและถูกพัฒนาขึ้นจากทีม Facebook

React
The library for web and native user interfaces

แนวคิดของ React คือ

  • แบ่งเว็บออกเป็นส่วนย่อยๆ เราเรียกมันว่า Component
  • Component ช่วยให้เราไม่ต้องเขียนโค๊ดซ้ำๆ
  • ในช่วงแรกๆ เริ่มมาจากแนวคิด Single Page Application (SPA)
  • JSX ที่มีความคล้ายกับ HTML แต่มันคือ JavaScript ที่มีความสามารถเพิ่มขึ้น

การสร้าง Component

function MyButton() {
  return <button>This is my button component</button>
}
  • Component คือ UI ที่มี logic ภายใน หรือเป็นแค่ markup อย่างเดียวก็ได้
  • เป็น function ธรรมดา ที่ return JSX
  • เราจะสร้าง Component โดยแบ่งเป็นส่วนเล็กๆ แค่ Button หรือ Link หรือจะสร้าง Component 1 อัน ให้ render ทั้งหน้า ก็ได้ (อยู่ที่เราออกแบบ)
  • แม้จะเป็น javascript function แต่ naming convention แนะนำให้ตั้งชื่อเป็น capital letter คือ ขึ้นต้นด้วยตัวพิมพ์ใหญ่ เช่น MyButton , Button , Image (จะได้ไม่ซ้ำกับ tag html ที่มีอยู่แล้ว)
  • เวลาที่เราจะ Return element ใน Component เราจะใช้ () กรณีมีหลายบรรทัด

การเรียกใช้งาน Component เหมือนกับ การใช้งาน tag html ปกติเลย

<MyComponent></MyComponent>

เพียงแค่ใน React เมื่อเราไม่มี content ใน element เราสามารถใช้ closing tag ได้เลยแบบนี้

<MyComponent />

3. JSX และ Component

  • JSX เป็น Markup Syntax หน้าตามัน คล้ายๆกับ HTML
  • เราสามารถแทรกโค๊ด JavaScript ไปได้ หรือสั่ง execute javascript ใน JSX ได้
  • ใน markup เวลาเราใช้ class เราจะใช้ className แทน
  • ต้องมี close tag เสมอ
  • Single Root Element ห้าม return multiple element (ต้อง wrap ให้เป็น 1 element)

ตัวอย่าง JSX ที่ผิด คือ ไม่สามารถ return multiple element แบบนี้ได้ และตัวอย่างที่ถูก คือ wrap เป็น 1 element

// ❌ return multiple element ไม่ได้
function MyComponent() {
  return (
    <h1>Hello World!</h1>
    <p>This is component!</p>
  )
}

// ✅ wrap มันด้วย div ซะ
function MyComponent() {
  return (
    <div>
      <h1>Hello World!</h1>
      <p>This is component!</p>
    </div>
  )
}

ในบางครั้ง เราไม่ต้องการ wrap มันด้วย div เพิ่ม เพราะมันจะทำให้ เวลาเรา render HTML เรามีโค๊ด div เพิ่มมา ทำให้ stylesheet ของเราผิดเพี้ยน เราสามารถ wrap element ด้วย การใช้ React.Fragment หรือ ตัวย่อคือ <> และ </> แบบนี้

function MyComponent() {
  return (
    <React.Fragment>
      <h1>Hello World!</h1>
      <p>This is component!</p>
    </React.Fragment>
  )
}

// หรือใช้ short hand
function MyComponent() {
  return (
    <>
      <h1>Hello World!</h1>
      <p>This is component!</p>
    </>
  )
}

การแสดงค่า JavaScript หรือการแทรกโค๊ด JavaScript ลงไปใน JSX เราจะใช้ {} ตัวอย่าง

function MyComponent() {
  const title = 'Hello World!'
  const message = 'This is component!'
  
  return (
    <div>
      <h1>{title}</h1>
      <p>{message}</p>
    </div>
  )
}

4. CSS

เราสามารถเพิ่ม Style ใน React ได้หลากหลายวิธีครับ

className

วิธีแรกคือใช้แบบ HTML/CSS ปกติได้เลย เพียงแต่ใช้ attribute ชื่อ className แทนที่ class แบบนี้

<img className="avatar" />

ที่มีไฟล์ css ดังนี้

/* In your CSS */
.avatar {
  border-radius: 50%;
}

Inline-Style

เป็นการแทรก style แบบ inline คล้ายๆ HTML เพียงแต่ style แบบนี้ ต้องเป็น object เท่านั้น ตัวอย่าง

const myStyle = {
  avatar: {
    borderRadius: '50%'
  }
}

<img style={myStyle.avatar} />

ข้อสังเกต ใน Style Object เราจะใช้ชื่อแบบ camelCase นะครับ เช่น borderRadius หรือ ตัวอย่างอื่นๆ คือ

  • font-sizefontSize
  • text-algintextAlign
  • font-familyfontFamily

และพวก properties value ถ้าไม่ใช่ number ก็จะเป็น string เช่นพวก สี, px ต่างๆ

CSS Modules

คล้ายๆกับ การใช้งาน CSS ปกติ เพียงแต่ว่า วิธีนี้ ข้อดีคือ แต่ละ Component จะถูก generate ชื่อแตกต่างกัน ทำให้ไม่ต้องกังวลว่า css เราจะไปชื่อซ้ำกับไฟล์อื่นๆ

วิธีการก็คือ ตั้งชื่อไฟล์ ลงท้ายด้วย .module.css เช่น [name].module.css ชื่อ style.module.css

.avatar {
  border-radius: 50%;
}

และในไฟล์ JSX ก็ import และเรียกใช้งานแบบนี้

// 1 import css มาไว้ที่ตัวแปร styles
import styles from './style.module.css'

function MyComponent() {
  return <img className={styles.avatar} />
}

เราจะสังเกตความแตกต่างจาก css ปกติ เมื่อเราเปิดเว็บ แล้วดู HTML Element จะเห็นว่าชื่อคลาส มันถูก generate จาก .avatar เป็นรูปแบบ hash

อื่นๆ (Third Party / Library)

  • SCSS/SASS - รองรับ SCSS/SASS ด้วย ซึ่งเราต้องติดตั้ง pre-processor เอง ส่วนนี้ผมไม่ได้ยกตัวอย่างนะครับ แค่พูดถึงว่า เราสามารถใช้ SCSS/SASS ได้เช่นกัน
  • JS in CSS / CSS in JS - ส่วนนี้ไม่ใช่ React แต่เป็นพวก Third Party ต่างๆ เช่น Styled Component, Emotion
styled-components
Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress 💅🏾
Emotion – Introduction

ส่วน CSS Library/ Framework ที่นิยมในโลกของ React.js (นอกจาก Bootstrap) คือ

Tailwind CSS - Rapidly build modern websites without ever leaving your HTML.
Documentation for the Tailwind CSS framework.
MUI: The React component library you always wanted
MUI provides a simple, customizable, and accessible library of React components. Follow your own design system, or start with Material Design.
Chakra UI - A simple, modular and accessible component library that gives you the building blocks you need to build your React applications.
Simple, Modular and Accessible UI Components for your React Applications. Built with Styled System
Ant Design - The world’s second most popular React UI framework
An enterprise-class UI design language and React UI library with a set of high-quality React components, one of best React UI library for enterprises

5. Rendering List

กรณีที่เราต้องการ render Component ที่คล้ายๆ กันเช่น Profile  หรือ list ของ สิ่งของต่างๆ เราสามารถใช้ JavaScript Array map ได้ ตัวอย่าง ที่เห็นบ่อยๆ เช่น เราจะแสดง <ul> และข้างในมี <li> มีข้อมูลดังนี้

<ul>
  <li>John Doe</li>
  <li>Chuck Norris</li>
  <li>Jane Doe</li>
  <li>Foo Bar</li>
  <li>Alice Bob</li>
</ul>

สิ่งที่ต้องทำคือ

  1. เปลี่ยนข้อมูล ให้อยู่ในรูปแบบ JavaScript Array
const names = [
  'John Doe',
  'Chuck Norris',
  'Jane Doe',
  'Foo Bar',
  'Alice Bob'
]

2. ใช้ map เพื่อแปลงให้เป็น array ของ JSX nodes ชื่อ users

const users = names.map(name => <li>{name}</li>)

3. จากนั้น ใน component เราก็สามารถ ใช้แบบนี้ได้

function MyComponent() {
  return <ul>{users}</ul>
}

นอกจากนี้ เราสามารถแทรก Map ใน JSX ตรงๆ ภายใน return function ได้เลย โดยไม่ต้องสร้างตัวแปรก็ได้ แบบนี้

function MyComponent() {
  return (
    <ul>
      {
        users.map(user => {
          return <li>{user}</li>
        })
      }
    </ul>
  )
}

ใน loop อย่าลืม return ด้วยนะครับ ตัวอย่าง

// ✅ arrow function แบบ return JSX (implicit return)
users.map(user => <li>{user}</li>)

// ❌ arrow function แต่ไม่ได้ return ค่า
users.map(user => {
  <li>{user}<li>
})

// ✅ return JSX
users.map(user => {
  return <li>{user}<li>
})

เราจะได้ Warning ว่า Each child in a list should have a unique "key" prop. ใน list items เราควรจะใส่ key ให้กับมันด้วย ตัวอย่าง จากปกติ ข้อมูล array string เราก็ทำเป็น data object โดยการใช้ id

const names = [
  { id: 1, name: 'John Doe' },
  { id: 2, name: 'Chuck Norris' },
  { id: 3, name: 'Jane Doe' },
  { id: 4, name: 'Foo Bar' },
  { id: 5, name: 'Alice Bob }
]

function MyComponent() {
  return (
    <ul>
      {
        users.map(user => {
          return <li key={user.id}>{user.name}</li>
        })
      }
    </ul>
  )
}

หรือถ้าไม่มี id จริงๆ ก็ใช้ index ของ array ก็ได้ แต่ก็ไม่แนะนำเท่าไหร่ (กรณีที่ไม่มี id หรือ unique อื่นๆ จริงๆ เท่านั้น)

users.map((user, index) => <li key={index}>{user}</li>)
Rendering Lists – React
The library for web and native user interfaces
อ่านเพิ่มเติม เรื่อง Rendering Lists

6. Condition Rendering

ใน JSX เราสามารถกำหนด condition ว่าจะ render Component ไหนหลายวิธี วิธีแรกคือ

If Else

ตัวอย่างการใช้ If Else ในการ render Component ถ้า score มากกว่า 50 ก็จะ render แบบนึง ถ้า น้อยกว่า 50 ก็จะอีกแบบ วิธีนี้จะใช้แบบ JavaScript คือการกำหนดเงื่อนไข และก็ assign JSX ไปเก็บไว้ที่ตัวแปรซักตัว

funciton MyComponent() {
  let result
  const score = 50
  
  if (score >= 50) {
    result = <p>You've passed!</p>
  } else {
    result = <p>Please try again next time</p>
  }
  
  return (
    <div>
      <p>Your result:</p>
      {result}
    </div>
  )
}

ใช้ ? operator

คล้ายๆ If Else แต่วิธีนี้ คือเราใช้ภายใน JSX จากตัวอย่าง If Else ถ้าเราใช้ ? operator จะเป็นแบบนี้

funciton MyComponent() {
  const score = 50
  
  return (
    <div>
      <p>Your result:</p>
      {score >= 50 ? (
        <p>You've passed!</p>
      ) : (
        <p>Please try again next time</p>
      )}
    </div>
  )
}

อธิบายเพิ่มเติม คิดว่าน่าจะงงแน่นอน ตัว syntax ของ ? operator หรือ ternary operator คือ

condition ? exprIfTrue : exprIfFalse

ถ้าเงื่อนไข True ก็ execute อันแรก ถ้า False ก็จะ execute อันที่สอง (หลัง colon) พอมาดูดีๆ ตัว JSX เรา เปรียบเทียบเป็น

score >= 50 ? // condition
<p>You've passed!</p> // exprIfTrue
<pPlease try again next time</p> //exprIfFalse

logical &&

ในบางครั้ง เราต้องการแค่ condition เป็น If ไม่ต้องการ​ Else เราสามารถใช้ && ได้ ตัวอย่าง syntax คือ

condition && exprIfTrue

ผมจะให้ Component แสดงข้อความ You've passed ถ้า score มากกว่า 50 และไม่ต้องแสดงอะไรเลย ถ้าคะแนนน้อยกว่า 50 ก็จะได้แบบนี้

funciton MyComponent() {
  const score = 50
  
  return (
    <div>
      <p>Your result:</p>
      {score >= 50 && (
        <p>You've passed!</p>
      )}
    </div>
  )
}

หรือตัวอย่างอื่นๆ อย่างเช่น ถ้าสมมติ เราทำระบบ Login ก็เช็คเงื่อนไข ถ้า logged in ก็ให้แสดงหน้า Dashboard ถ้าไม่ได้ Login ก็ให้แสดงหน้า Login แบบนี้

const isLoggedIn = true // สมมติ ได้ค่ามาจาก api

<div>
  {isLoggedIn && <Dashboard />
</div>

7. Events

ต่อมาเรื่องของ Event ใน React เบื้องต้น เราจะพูดถึง onClick ก็คือ รับ event เวลาที่ user ทำการ click ปุ่ม นั่นเอง

โดยปกติ ถ้าเป็น HTML & JS เราจะรับ events ผ่าน onclick หรือใช้ addEventListener()

<button onclick="doSomething()">Click me</button>

<button id="click-me">Click me!</button>

<script>
const button = document.getElementById('#click-me')

button.addEventListener('click', () => {
  // do something
})
</script>

ใน React เราก็จะใช้คล้ายๆ กันเพียงแต่ว่า onclick จะเป็น onClick (camelCase) และรับเป็น handler function แทนที่จะเป็น string

export default function MyButton() {
  function handleClick() {
    alert('You clicked me!')
  }

  return (
    <button onClick={handleClick}>
      Click me
    </button>
  )
}
จะเห็นว่าเราส่งแค่ function handleClick  เราจะไม่ execute มันนะครับ แบบนี้คือผิด onClick={handleClick()}

นอกจากนี้ events เราสามารถใช้ anonymous function ไปเลยก็ได้ ถ้าเกิดว่า function นั้นไม่ได้ต้องการอะไรซับซ้อน

<button onClick={() => alert('You clicked me!')}>
  Click me
</button>

นอกจากนี้ ตัว handler function เรายังสามารถรับค่า ที่เราต้องการได้ เช่น สมมติ เรา loop list of items และต้องการกด click เพื่อแสดง id ของ item นั้นๆ ตัว handler function เราก็รับ id มาแบบนี้

export default function MyButton() {
  function handleClick(id: number) {
    alert(`You clicked ${id}`)
  }

  return (
  <>
  {
    items.map(item => (
      <button onClick={() => handleClick(item.id) }>
        Click me
      </button>
    ))
  }
  </>

  )
}

8. Props

React Component จะใช้ props ในการ รับ-ส่ง ข้อมูล ระหว่าง Component ตัว Parent Component สามารถที่จะส่งข้อมูล ไปหา Child Component ได้ ผ่าน props (เหมือน HTML attributes) แต่ต่างกันตรงที่ เราสามารถ ส่งค่า JavaScript หรือ object array หรือ function ก็ได้

ตัวอย่าง ผมลองเปลี่ยน <li> ใน MyComponent แยกไปเป็น Component ใหม่ ชื่อ Item

function Item() {
  return <li>John Doe</li>
}

ทีนี้ เวลาเราใช้ Item ก็จะเป็นแบบนี้

const names = [
  { id: 1, name: 'John Doe' },
  { id: 2, name: 'Chuck Norris' },
  { id: 3, name: 'Jane Doe' },
  { id: 4, name: 'Foo Bar' },
  { id: 5, name: 'Alice Bob }
]

function Item() {
  return <li>John Doe</li>
}

function MyComponent() {
  return (
    <ul>
      {
        users.map(user => {
          return <Item />
        })
      }
    </ul>
  )
}

ทีนี้จะสังเกตเห็นว่า ทุกๆ Item มันยังแสดงแค่ John Doe ยังไม่ได้เป็นค่าจริงๆ ที่เราอยากให้เป็น วิธีการก็คือ สร้าง props ขึ้นมาใหม่ ผมตั้งชื่อว่า name ละกัน และส่งไปแบบนี้

<Item name={user.name} />

การรับค่า Props โดยปกติทุกๆ Component ตัว function จะรับ props เป็น parameter อยู่แล้ว สามารถรับค่าผ่าน props ได้ แบบนี้

  1. ทำการกำหนด Props Types ให้มันก่อน เช่น name เป็น string
interface Props {
  name: string
}

2. กำหนด Type เป็น Props ที่เราสร้างไว้

function Item(props: Props) {
  return <li>{props.name}</li>
}

ซึ่งถ้า ตัว Parent เราส่ง props ชื่ออะไร ตัว Child Component เราก็สามารถ access ชื่อนั้นๆ ผ่าน props ได้ เช่น ถ้าเรามีส่ง name, id, age อะไรก็แล้วแต่

function Item(props) {
  const name = props.name
  const id = props.id
  const age = props.age
  
  // const { name, id, age } = props
  
  return <li>{name}</li>
}

ส่วนใหญ่แล้ว ส่วนใหญ่จะเขียนเป็น destructoring object มากกว่า แบบนี้

function Item({ name }) {
  return <li>{name}</li>
}

เราสามารถ set default value ให้กับ props ได้ กรณีที่ถ้า Parent Component ไม่ได้ส่ง props มา เราก็ใช้ default value แบบนี้

function Item({ name, age = 0 }) {
  return <li key={name}, {age} yrs.</li>
}

Code หลังจากใช้ default value

const names = [
  { id: 1, name: 'John Doe', age: 25 },
  { id: 2, name: 'Chuck Norris', age: 30 },
  { id: 3, name: 'Jane Doe', age: 23 },
  { id: 4, name: 'Foo Bar', age: 35 },
  { id: 5, name: 'Alice Bob }
]

function Item({ name, age = 0 }) {
  return <li key={name}, {age} yrs.</li>
}

function MyComponent() {
  return (
    <ul>
      {
        users.map(({ id, name }) => {
          return <Item key={id} name={name} />
        })
      }
    </ul>
  )
}

9. State

React State คือข้อมูลที่เราจะใช้ภายใน Component ต่างจาก Props ที่ใช้ส่งข้อมูลข้ามกันระหว่าง Component เราอ่าน Props จากภายนอก Component แต่ State อยู่ภายใน Component

  • ทุกครั้งที่ State มีการเปลี่ยนแปลงค่า ตัว Component จะทำการ re-render
  • การสร้าง state ใน React ตัว useState จะรับค่า initial value และ return ค่า Array กลับมา
import { useState } from 'react'

const states = useState(0)

// states จะได้เป็น array
// array[0] = state
// array[1] = function ที่ setState

เรามักจะเขียนแบบนี้ (Destructoring array)

import { useState } from 'react'

const [count, setCount] = useState(0)
  • เราสามารถตั้งชื่อ state เป็นอะไรก็ได้
  • ตัว function ที่ setState เช่นกัน สามารถตั้งชื่ออะไรก็ได้ แต่ naming convention นิยมตั้ง setName เช่น ถ้า count ก็ setCount ถ้า state ชื่อ name ก็ setName เป็นต้น

การ setState

ส่วนใหญ่ การ setState จะทำก็ต่อเมื่อมี event หรือ condition อะไรซักอย่าง เช่น user click ปุ่ม Button หรือการ initial render ครั้งแรก (useEffect() บทเรียนถัดไป)

ทุกๆ ครั้งที่ state มีการเปลี่ยนแปลงค่า ตัว Component จะทำการ re-render ใหม่ ฉะนั้น ระวังเรื่องการ update state จน infinity loop ด้วย

ตัวอย่าง เมื่อเมาท์คลิ๊ก จะทำการ set state และเปลี่ยนค่า Counter ซึ่ง ก็เหมือนกับโคีด default ที่เราขึ้นโปรเจ็ค Vite นั่นเอง (ค่า count จะเพิ่มขึ้นทีละ 1 จากการกด)

import { useState } from 'react'

function App() {
  const [count, setCount] = useState(0)
  
  return (
    <button onClick={() => setCount((count) => count + 1)}>
      count is {count}
    </button>
  )
}

Object State

หากเราต้องการเก็บ state เป็น Object สิ่งแรกที่เราต้องทำคือ กำหนด Type ให้กับ Object ก่อน ว่าจะเป็นอย่างไร เช่น สมมติ เราจะเก็บ user เรารู้ว่า user object ต้องมี id กับ name เราก็กำหนด Type ก่อน แบบนี้

interface User {
  id: number
  name: string
}

จากนั้น เวลาเรา initial state เราจะทำแบบนี้ (ใช้ Generic type)

const defaultUser = { id: -1, name: 'Default' }

const [user, setUser] = useState<User>(defaultUser)

หรือไม่มี default User ก็ให้เป็น undefined ก็จะเป็นแบบนี้ (ใช้ TypeScript Union (|) หมายถึง User หรือ undefined

const [user, setUser] = useState<User | undefined>()

การอัพเดท Object State

หากต้องการอัพเดท Object State เราจะใช้วิธีการสร้าง new object ขึ้นมา เพื่อ set state

const handleOnClick = () => {
  const newUser = {
    id: 2,
    name: 'New name'
  }
  
  setUser(newUser)
}

กรณี ต้องการอัพเดท เพียงแค่ name เราสามารถใช้ Spread Operator ได้

const handleOnClick = () => {
  const newUser = {
    ...user,
    name: 'New name'
  }
  
  setUser(newUser)
}

หรือใช้ Object destructoring ตอน setUser เลยแบบนี้

setUser({ ...user, name: 'New name' })

10. Props & State and Events

State กับ Props ต่างกันอย่างไร?

  • State เป็นค่าที่ใช้ภายใน Component อย่างเดียว
  • เมื่อ state มีการเปลี่ยนแปลง ตัว component จะ re-render
  • ตัว Props เป็นค่าที่เอาไว้ส่งข้อมูลระหว่าง component
  • Props เปลี่ยน ตัว component ก็ re-render
  • Props สามารถส่งค่าได้ทั้งตัวแปร หรือ function ก็ได้
  • Component แม่ มีค่า props/state เปลี่ยน ตัว children component ก็จะ re-render เช่นกัน

Children Props

ปกติ เวลาเราจะส่งข้อมูลไปให้ components เราก็จะกำหนด เป็น props ใช่มั้ย แต่มี props ตัวนึงที่ไม่ต้องกำหนด เพราะมันเป็น children props ก็คือ สามารถเข้าถึงได้ผ่าน props.children ตัวอย่าง

<MyComponent>
  <h1>This is children header</h1>
  <p>This is paragraph</p>
</MyComponent>

ข้อมูลข้างใน <MyComponent> ทั้งหมด ก็คือ children ทีนี้ตัว MyComponent เวลาเราสร้าง ก็จะมีหน้าตาแบบนี้

export default function MyComponent({ children } ) {
  return (
    <>
      {children}
    </>
  )
}

Sharing state กับ components

ปกติเวลาที่เรา share state ระหว่าง component ทำได้แค่ จาก parent -> ไป children ใช่มั้ยครับ เราไม่สามารถที่จะส่งจาก children ไปหา parent ได้

กรณีที่เรามี component ซ้อนกันหลายๆชั้น และส่ง state ไปเป็นทอดๆ คงลำบากน่าดู มีวิธีนึงที่แนะนำก็คือ เรื่องของ lift up state ก็คือพยายาม นำ state ไปไว้ที่ตัว parent component แล้วค่อยส่งข้อมูล props ไป

Sharing State Between Components – React
The library for web and native user interfaces

การส่ง handle events function ผ่าน Props

ใช้ handle event function ผ่าน props เพื่อเปลี่ยน state ของ parent วิธีนี้คือ เราสามารถ ส่ง handler function ผ่าน props ไปให้ children ได้ และเมื่อมี event เกิดขึ้นตัว parent component ก็รับ event และทำการเปลี่ยนแปลงค่า state ตัวอย่างเช่น

import { useState } from 'react';

function MyButton({ handleClick }) {
  return (
    <button onClick={handleClick}>
      Click me!
    </button>
  );
}

export default function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <>
      You pressed me {count} times
      
      <MyButton handleClick={handleClick} />
    </>
  );
}

จบแล้วสำหรับตอนที่ 1 สำหรับตอนนี้ ก็จะเป็นพื้นฐานของ React เบื้องต้นนะครับ ลองฝึกฝน และทบทวนกันดู หากมีคำถาม สามารถสอบถามเข้ามาได้ตลอด ทั้ง Facebook Page หรือ Social media อื่นๆ ตามช่องทางที่ให้ไว้ในเว็บนะครับ

Workshop

ฝึกทำ Workshop & Mini Project ด้วย React.js

[Workshop] ทำแอพคำนวณ BMI ด้วย React.js
สวัสดีครับ Workshop นี้เป็นหนึ่งใน Workshop ในคอร์สเรียน สอนเขียนเว็บด้วย React.js นะครับ ตัวแอพนี้จะเป็น BMI Calculator หรือโปรแกรมคำนวณค่าดัชนีมวลกายนั่นเอง หลังจากที่เราเรียนคอร์ส Intro to React ตอนที่ 1 ไปแล้ว ก็นำเอาสิ่งที่เรียน

Happy Coding ❤️

Tags

Chai Phonbopit

เป็น Web Dev ทำงานมา 10 ปีหน่อยๆ ด้วยภาษา JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจ Web3, Crypto และ Blockchain เขียนบล็อกที่ https://devahoy.com