"use server"
export async function getData() {
const data = await fetch('http://localhost:3002/getData')
if (!data.ok) {
throw new Error('Failed to fetch data')
}
return data.json()
}
ในบทความนี้ผมจะมาแนะนำ การดึงข้อมูลจาก Backend โดยใช้ Fetch ซึ่งจะมีการใช้ useState ในการติดตามสถานะ ว่าดึงข้อมูลออกมาแล้วหรือยัง เหตุผลที่ต้องใช้ useState ก็คือ ฝั่ง Client-Side นั่นหากเราดึงข้อมูล โดยไม่ติดตามสถานะ จะทำให้ React ดึงข้อมูลอยู่ตลอดเวลา ไม่รู้ว่าควรหยุดหรือไม่ ทำให้เกิดสิ่งที่เรียกว่า "Waterfall Fetch" ซึ่งคือการดึงข้อมูลไม่จบไม่สิ้นนั่นเอง สุดท้ายจะทำให้ Web Brower เกิดอาการค้างขึ้นมาได้
ตัวอย่างที่ผมจะทำก็คือ การดึงข้อมูล JSON ดังนี้มาจาก Backend และนำมาแสดงในหน้า /about
{
"persons": [
{
"name": "Sunny Jirakit",
"major": "Computer Science",
"age": 22
}
]
}
ซึ่งผมจะแบ่งการทำงานเป็นสองฝั่ง ได้แก่ ฝั่ง Server Side ผมจะมีฟังก์ชั่นที่ชื่อ getData ใช้ Fetch และ Return ออกมาเป็น JSON และฝั่งของ Client ซึ่งผมจะเขียนลงไปในไฟล์ page.tsx เดิมของเราเลย
ก่อนแรกผมจะทำการสร้างไฟล์สำหรับ Server Side Function ขึ้นมาก่อน โดยจะตั้งชื่อว่า server.tsx อยู่ใน /src/app/about โดยจะมีการสร้างฟังก์ชั่น เพื่อใช้ในการ Fetch ดังนี้ โดยเราจะใส่ "use server" ลงไปด้วย เพื่อให้ Next อยู่ว่าเราต้องการให้สิ่งที่อยู่ในไฟล์นี้ประมวลผลจาก Server
"use server"
export async function getData() {
const data = await fetch('http://localhost:3002/getData')
if (!data.ok) {
throw new Error('Failed to fetch data')
}
return data.json()
}
กลับมาที่ไฟล์ /src/app/about/page.tsx เราจะมีการ Import useState และ useEffects เข้ามา ใช้ในการเก็บค่าสถานะ
โดยผมจะมี สถานะสองสถานะ ได้แก่
isLoad สำหรับเช็คว่าขณะนี้กำลังโหลดข้อมูลอยู่หรือไม่ โดยจะมีค่าเริ่มต้นเป็น true และเมื่อดึงข้อมูลเสร็จแล้ว จึงจะเปลี่ยนเป็น false
data สำหรับเก็บข้อมูลที่เราดึงมาจาก backend ซึ่งจะสามารถเรียกใช้ภายใน funtion ได้ โดยมีค่าเริ่มต้นเป็น array ว่าง []
เวลาเราจะสร้างสถานะใหม่ เราก็จะสร้าง array ขึ้นมา โดยมีสองตัวแปรอยู่ด้านใน ตัวแรก "isLoad" เป็นตัวเก็บ Boolean ตัวที่สอง "setLoad" เป็นฟังก์ชั่นที่ใช้เปลี่ยนค่าของ isLoad จากนั้นเราจะใช้ = useState(true) หมายถึง ค่าเริ่มต้นของ isLoad จะเป็น true นั่นเอง
const [isLoad, setLoad] = useState(true)
เวลาเราจะกำหนดค่าใหม่ให้กับ isLoad เราก็จะเรียกใช้ setLoad ดังนี้
setLoad(false)
เราก็จะสามารถสร้างตัวติดตามสถานะ ได้ และสามารถเปลี่ยนค่าของสถานะนั้นๆ หลาย ๆ คนอาจจะใช้ useState ในการนับ 12345... แบบนี้ก็ได้ เราสามารถนำไปประยุกต์ใช้ได้หลายรูปแบบ
กลับไปที่เรื่องของเรา ผมจะ import ฟังก์ชั่น getData() จากไฟล์ server.tsx มาอยู่ใน page.tsx และกำหนด useState ที่ผมต้องการใช้ขึ้นมา
"use client"
import { useEffect, useState } from "react"
import { getData } from "./server"
export default function About() {
const [isLoad, setLoad] = useState(true)
const [data,setData] = useState([])
useEffect(() => {
getData().then((persons_data) => {
setData(persons_data)
setLoad(false)
})
}, [])
if(isLoad) {
return ("Loading...")
} else {
return (
<>
<h1>About Page</h1>
</>
)
}
}
จะเห็นได้ว่าผมมีการใช้ useEffects() ด้วย ซึ่ง useEffects ก็คือสิ่งที่หากเราใส่ ฟังก์ชั่น อะไรบางอย่างเข้าไป มันจะถูกเรียกใช้เมื่อหน้าเว็บถูกรันโดยอัตโนมัติ โดยที่ผู้ใช้ไม่ต้องมีการเรียกใช้เอง (คล้ายกับ ngInit ของ Angular)
เนื่องจากผมต้องการให้ เมื่อเราเปิดหน้าเว็บขึ้นมาฟังก์ชั่น getData() จะถูกเรียกใช้ทันที จากนั้นเอาค่าที่ return ออกมาจาก getData เก็บไว้ในตัวแปร persons_data และเราจะใช้ setData, setLoad ที่เราสร้างขึ้นมา ใส่ค่าใหม่เข้าไปใน useState
แต่ตอนนี้เรายังไม่มีส่วนที่นำ data จาก useState มาแสดง ผมก็เลยจะสร้าง ฟังก์ชั่นใหม่ชื่อ ViewPersonalData ที่จะรับค่า data เข้ามาในฟังก์ชั่น และใช้ map() ในการ Loop ข้อมูลออกมาแสดง โดยจะมีการกำหนด type ของข้อมูลด้วย หน้าตาของฟังก์ชั่น ก็จะเป็นประมาณนี้
type Person = {
name: string,
major:string,
age:number
}
export function ViewPersonalData({data}:any) {
return (
data.persons.map((row:Person) => (
<div key={row.name}>
<h4>My name is {row.name}</h4>
<p>I am {row.age} year old.</p>
<p>I am currently studying major of {row.major}.</p>
</div>
))
)
}
จากนั้นเราจะเรียก ViewPersonalData พร้อมใส่ Attribute data และข้อมูล data จาก useState ลงไปด้วย
"use client"
import { useEffect, useState } from "react"
import { getData } from "./server"
type Person = {
name: string,
major:string,
age:number
}
export function ViewPersonalData({data}:any) {
return (
data.persons.map((row:Person) => (
<div key={row.name}>
<h4>My name is {row.name}</h4>
<p>I am {row.age} year old.</p>
<p>I am currently studying major of {row.major}.</p>
</div>
))
)
}
export default function About() {
const [isLoad, setLoad] = useState(true)
const [data,setData] = useState([])
useEffect(() => {
getData().then((persons_data) => {
setData(persons_data)
setLoad(false)
})
}, [])
if(isLoad) {
return ("Loading...")
} else {
return (
<>
<h1>About Page</h1>
<ViewPersonalData data={data} />
</>
)
}
}
ทีนี้เว็บไซต์ของเราก็จะนำข้อมูลออกมาแสดงเป็นที่เรียบร้อย
I am 22 year old.
I am currently studying major of Computer Science.
อ้างอิง:
