สวัสดีครับ วันนี้ผมมาลองทำ Example Project ด้วยการลองใช้งาน SWR นะครับ โดยปกิแล้ว เวลาเรา Fetch API จาก Server เราจะใช้พวก native fetch ที่มากับ Browser หรือบางคนใช้ polifill unfetch หรือ axios อะไรก็ว่าไป

อีกเวอร์ชัน่คือแบบ Video Youtube อาจจะเห็นภาพมากกว่าครับ

ทีนี้ บทความนี้ ผมมาแนะนำ Library ตัวนึงชื่อว่า swr ครับ และเป็นกึ่งๆ สรุปหัวข้อครับ อาจจะไม่ได้มี Demo หรือ Example เหมือนแบบ Video นะครับ

SWR คืออะไร?

มันย่อมาจาก Stale While Revalidate หากใครคุ้นเคย มันจะเหมือนกับที่เรากำหนด Cache Control ที่ Headers ใช่มั้ย เพราะว่ามันคือการบอกว่าให้โหลดข้อมูลจาก cache ก่อนทำการ fetch api นั่นเอง

ซึ่งวิธีปกติ ถ้าเรา fetch(api) เราจะยิง request ไปหา Server แต่ถ้า เรา มี cache เราก็ไม่ต้องเสียเวลาไปยิง ก็ได้ข้อมูลมาเลย หรือเราอาจจะเอาข้อมูลจาก cache มาแสดงก่อน แล้วถึง ยิง request ไป เมื่อได้ response กลับมา ถึงมาอัพเดท ค่าเก่า ก็ทำให้ User ไม่ต้องรอ response นั่นเอง

ฉะนั้นสรุปสิ่งที่ SWR ทำคือ

  1. ทำการดึง data จาก cache มาให้เราก่อน (stale)
  2. จากนั้นก็ ยิง request ไปที่ API (revalidate)
  3. นำข้อมูลจาก API ที่ได้มาอัพเดทกับข้อมูลเดิม

อ่านเพิ่มเติมได้ที่นี่เลยครับ https://swr.now.sh/

Basic Data Loading

ก่อนใช้งานก็ติดตั้งปกติครับ

npm install swr

จากนั้นโค๊ดด้านล่างคือ

import useSWR from 'swr'

function Profile () {
  const { data, error } = useSWR('/api/user', fetch)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

สิ่งที่ useSWR ทำคือ รับ parameter เป็น key และ fetcher ที่เป็น function ดังนั้น จะเห็นว่า ค่า argument ที่เราส่งไปคือ api/user เป็น key และ fetch เป็น fetcher function นั่งเอง

response กลับมาคือ data และ error กรณีที่มี error หรือ 400, 500 หรือ throw error ต่างๆ

Fetcher

Fetcher เป็นแค่ function ที่รับ key หรือ object แล้ว return Promise กลับไป ฉะนั้นตัว fetcher เราจะใช้พวก fetch API ธรมดา หรือ unfetch, isomophic-fetch, request หรือ axios ก็ได้ แล้วแต่เราเลย เช่นใช้ Native Fetch API

const fetcher = key => fetch(key).then(res => res.json())

หรือจะใช้ axios ก็ได้เช่น

import axios from 'axios'

const fetcher = (...args) => axios(...args).then(res => res.data)

SWR Options

ต่อมาคือการที่เราสามารถใส่ Option ลงไปได้ เช่น

const options = {
  fetchInterval: 1000
}
const { data, error } = useSWR('/api/user', fetch, options);

ตัว SWR มันจะทำการ Polling data ให้เรา คือการ fetch ทุกๆ 1 วินาทีนั่นเอง หรือแม้แต่จะ revalidateOnFocus (default คือ true) มันจะทำการ revalidate ทุกครั้งที่ browser นั้น focus แบบสลับแท็ปกลับมาไรงี้ ส่วน Options อื่นๆ มีอีกหลายตัวเลย สามารถอ่านเพิ่มเติมได้จาก Documentation ของ swr ได้เลยครับ https://github.com/zeit/swr

Global Config

ตัว SWR เราสามารถทำ Config แบบ global ที่เดียวก็ได้ เป็น Provider ครอบ component ของเราได้เลย เช่น

import { SWRConfig } from 'swr'

const fetcher = (...args) => fetch(...args).then(res => res.json())

function App () {
  return (
    <SWRConfig 
      value={{
        refreshInterval: 3000,
        fetcher
      }}
    >
      <MyApp />
    </SWRConfig>
  )
}

Mutate

กรณีที่เราต้องการ ให้ผู้ใช้งานรู้สึกว่าไม่ต้องรอ หรือเห็นผลเลย จากการเปลี่ยนแปลงค่า เช่น อัพเดท Profile แก้ไขค่าอะไรก็แล้วแต่ เราสามารถใช้ local state ได้ โดยใช้ mutate นั่นเอง ตัวนี้ swr จะแสดง local state ก่อน จากนั้นถึง ส่งข้อมูลไปที่ API จริงๆ จากนั้น ค่อยเอาผลลัพธ์กลับมาอัพเดท หรือแสดงผล

การใช้งานก็คล้ายๆ useSWR ครับ เช่น

import useSWR from 'swr'

function Profile () {
  const { data, mutate } = useSWR('/api/user', fetcher)

  return (
    <div>
      <h1>My name is {data.name}.</h1>
      <button onClick={async () => {
        const newName = data.name.toUpperCase()
        // send a request to the API to update the data
        await requestUpdateUsername(newName)
        // update the local data immediately and revalidate (refetch)
        // NOTE: key is not required when using useSWR's mutate as it's pre-bound
        mutate({ ...data, name: newName })
      }}>Uppercase my name!</button>
    </div>
  )
}

โค๊ดนี้จาก SWR Doc คือ กรณีเรา send request เพื่อ PUT/PATCH ไปที่ API แต่ไม่ต้องรอผลลัพธ์ เราสามารพสั่ง mutate เพื่ออัพเดทค่า data ที่ได้ตอน fetch ทีแรกได้เลย เพื่อทำการ อัพเดท local data และ revalidate ค่าครับ

สรุป

จะเห็นได้ว่าบทความนี้ ผมแค่ยกตัวอย่างคร่าวๆ มาให้ดูเป็น Overview และโค๊ดส่วนใหญ่ ก็สามารถหาอ่าน ได้จาก README ของ SWR เองอยู่แล้ว หากได้ลองใช้แล้ว ก็ทำให้เราไม่ต้องมาใช้ useEffect() เพื่อ fetch ข้อมูล หรือจัดการพวก local state, จัดการ state หรือ side effect ต่างๆ มากนัก และมันก็ยังเหมาะกับ Next.js อีกด้วย

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

หากติดปัญหา ตรงไหน สอบถาม หรือมีข้อเสนอแนะ ยินดีมากครับ

ขอบคุณครับ

Happy Coding