کتابخانه SWR در ری اکت و Next.js + هوک useSWR !

·

13 min read

کتابخانه SWR در ری اکت و هوک useSWR یکی از بهترین ابزار هایی هستن که به ما در مبحث Data Fetching ( دریافت اطلاعات از سرور ) کمک میکنن ..

بدون شک داده ها یکی از مهمترین اجزای تشکیل دهنده یک صفحه وبسایت هستن. این داده ها ( اطلاعات ) در سرور ذخیره میشن و ما باید این اطلاعات رو از سرور دریافت کنیم و اطلاعات دریافتی رو به کاربر نشون بدیم.

به این پروسه ی دریافت اطلاعات از سرور، Data Fetching میگیم.

روش های خیلی زیادی برای Data Fetching تو فِرانت اِند وجود داره که با استفاده از این تکنولوژی ها میتونیم داده های وبسایتمون رو از سرور خودمون دریافت کنیم.

تو جاوا اسکریپت از ()Fetch میشه برای Data Detching استفاده کرد. کتابخونه Axios هم یکی از بهترین کتابخونه هایی هست که میشه ازش برای دریافت اطلاعات از سرور استفاده کرد.

خیلی خیلی مهمه که از چه تکنولوژی برای Data Fetching استفاده میکنیم، چراکه انتخاب یک تکنولوژی مناسب و خوب برای دریافت اطلاعات از سرور، باعث تجربه کاربری عالی و Performance خوب اپیکیشن ما میشه.

اما تکنولوژی هایی بهتر از Fetch و Axios برای Data Fetching هم وجود دارن 🙂

با فِرانت اِندی همراه باشید که میخوایم استفاده از SWR در React و هوک useSWR در React و Next.js بررسی کنیم 🙂

کتابخانه SWR چیست ؟

SWR یکی از بهترین و قدرتمندترین کتابخونه های فِرانت اِند برای Data Fetching ( دریافت داده از سرور ) هست که روز به روز محبوبیتش داره بیشتر میشه.

عبارت SWR مخفف stale while revalidate هست.

در واقع یک کتابخونه سبک هست که به ما اجازه میده از سمت کلاینت عملیات Data Fetching رو انجام بدیم.

خود SWR یک هوک کاربردی به اسم useSWR در اختیارمون میزاره که کار کردن با SWR رو خیلی راحت تر و لذت بخش تر میکنه.

SWR در ری اکت امکانات و قابلیت های خیلی زیادی به ما میده مثل کش کردن داده ها ( Cashing ) ، اعتبار سنجی مجدد ( revalidation ) ، Pagination ، پیش بارگذاری ( Preloading ) ، DevTools برای تجربه کاربری بهتر و ..

امکاناتی که کتابخانه SWR به ما میده باعث میشه اپیکیشن ما خیلی بیشتر از قبل بهینه بشه و همین بهینه بودن باعث تجربه کاربری ( UX ) بهتر میشه.

کتابخانه SWR در Next.js

کتابخانه SWR در Next.js هم پشتیبانی میشه 🙂 تو این مقاله هیچ تفاوتی نداره که شما با React کار میکنید یا Next.js ، این مقاله برای هر دو مورد بصورت یکسان نوشته شده.

پس اگر از Next.js استفاده میکنید، میتونید از SWR در Next.js با خیال راحت استفاده کنید 🙂

استفاده از SWR در ری اکت

حقیقتا یکی از بزرگترین مزایای SWR در React ، ساده بودنشه ! استفاده از کتابخانه SWR خیلی سادس ..

شکل کلی استفاده از هوک useSWR در ری اکت بصورت زیر هست :

const { data, error } = useSWR(key, fetcher);

اگه به تیکه کد بالا دقت کنید، میبینید که هوک useSWR از ما 2 آرگومان میگیره :

  • آرگومان اول key هست :

تو این بخش باید یک key به هوک useSWR در ری اکت بدیم. معمولا خود API Endpoint رو به عنوان key به هوک useSWR میدیم.

  • آرگومان دوم fetcher هست :

در واقع fetcher یک Async Function هست که عملیات دریافت داده از back-end رو برای ما انجام میده.

اما اگه به تیکه کد بالا دقت کنید، میبینید که useSWR در React دو مقدار برای ما return کرده :

  • اولین مقدار data هست :

مقدار data همون چیزی هست که از back-end توسط API دریافت میکنیم. ( مثلا لیست کاربران ، محصولات و .. )

  • دومین مقدار error هست :

دومین مقدار یک error هست . این مقدار فقط درصورتی که دریافت داده از back-end با خطا مواجه بشه ، پر میشه. در غیراینصورت error خالی خواهد بود.

البته دو مقدار دیگه هم توسط هوک useSWR برای ما return میشه که در ادامه راجبشون صحبت میکنیم ( مقدار isLoading و isValidating )

یه مثال واقعی از SWR در ری اکت

برای اینکه بتونیم نحوه استفاده از SWR در React رو بررسی کنیم ، نیاز داریم که یک پروژه ری اکتی بسازیم .

برای ساخت پروژه ری اکت از دستور زیر استفاده میکنیم :

npx create-react-app my-swr

یا اگه از npm استفاده نمیکنید با Yarn اینکار رو بکنید :

yarn create react-app my-swr

دستور بالا رو اجرا میکنیم و منتظر میمونیم تا کتابخونه React برای ما نصب بشه.
بعد از نصب کامل ری اکت ، به مسیر روت پروژه برید ( با دستور cd یا بصورت دستی ) و دستور زیر رو برای اجرای پروژه React تو کنسول اجرا کنید :

npm start
// or
yarn start

حالا باید 2 کتابخونه swr و styled-components که تو این آموزش بهشون احتیاج داریم رو نصب کنیم :

npm install styled-components swr

اگه از npm استفاده نمیکنید ، با Yarn اینکار رو بکنید :

yarn add styled-components swr

ساخت سرور تستی

برای اینکه بتونیم با SWR از سرور یکسری اطلاعات رو دریافت کنیم، نیاز به یک سرور داریم. ما این سرور رو با express میسازیم. این سرور قراره یک لیست از کاربران به ما بده.

تو روت پروژه خودمون باید یک فولدر به نام server بسازیم .

حالا برای ساخت یک پروژه ی Node.js دستور زیر رو باید اجرا کنیم :

npm init -y

یا با Yarn این دستور رو اجرا کنید :

yarn init -y

حالا باید پکیج های زیر رو که لازمه ی ساخت سرور تستی هستن، نصب کنیم:

npm install express cors nodemon

یا با Yarn این دستور رو اجرا کنید :

yarn add express cors nodemon

خب تا اینجای کار عالی پیش رفتیم 🙂

حالا باید داخل پوشه server خودمون یک فایل به اسم index.js بسازیم و کد زیر رو داخلش قرار بدیم :

const express = require("express");
const cors = require("cors");
const app = express();

app.use(cors());
const data = [
    {
        name: "عاطفه",
        age: 27,
        avatar: "https://frontendi.com/wp-content/uploads/2023/08/frontendi-logo-3.png",
    },
    {
        name: "رضا",
        age: 15,
        avatar: "https://frontendi.com/wp-content/uploads/2023/08/frontendi-logo-3.png",
    },
    {
        name: "سارا",
        age: 21,
        avatar: "https://frontendi.com/wp-content/uploads/2023/08/frontendi-logo-3.png",
    },
];

app.get("/", (req, res) => {
    res.json(data);
});

app.listen(3001, () => {
    console.log("App listening on port 3001");
});

تو تیکه کد بالا، ما یک سرور تستی تو express.js ساختیم و یک لیست از کاربران داخلش قرار دادیم.

میخوایم زمانیکه از این API ساخته شده استفاده کردیم ، لیست کاربران رو از سرور بتونیم دریافت کنیم که در فِرانت اِند نمایششون بدیم.

برای اینکه به مشکلات cors در فِرانت اِند برنخوریم ، باید cors رو به فایل package.json اضافه کنیم :

{
  "name": "server",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.18.2"
  },
  "scripts": {
    "dev": "nodemon index.js"
  }
}

حالا دستور زیر رو برای اجرا شدن سرورِ back-end خودمون باید اجرا کنیم :

npm run dev
// or :
yarn dev

اگه این مراحل رو درست پیش رفته باشید ، باید توی کنسول عبارت App Listening on port 3001 رو ببینید.

خب بلاخره ساخت سرور تستی توم شد:)

بریم سراغ Data Fetching …

دریافت داده از سرور با متود Fetch

تو جاوااسکریپت میشه از متود Fetch برای دریافت داده و اطلاعات از بک اند استفاده کرد.

ما میخوایم با متود Fetch لیست کاربران خودمون رو از سرور تستی که ساخته بودیم دریافت کنیم.

برای اینکار باید به پوشه src بریم و داخل این پوشه ، یک پوشه به نام components ایجاد کنیم. داخل این پوشه یک فایل به نام Home.js ایجاد کنید و کد زیر رو داخلش قرار بدید :

import React from "react";
import { useState, useEffect } from "react";

const Home = () => {
    const [users, setUsers] = useState(null); // برای ذخیره کردن لیست کاربرانی که از سرور دریافت میکنیم

    useEffect(() => { // دریافت لیست کاربران از سرور
        async function fetchUsers() {
            const response = await fetch("http://localhost:3001");
            const data = await response.json();
            setUsers(data);
        }
        fetchUsers();
    }, []);

    if (!users) return <h2>Loading...</h2>;
    return (
        <>
            <div> // نمایش لیست کاربران
                {users.map((user, index) => {
                    return <h2 key={index}>{user.name}</h2>;
                })}
            </div>
        </>
    );
};

export default Home;

تو تیکه کد بالا ما لیست کاربران رو از سروری که ساختیم دریافت میکنیم و در نهایت نشون میدیم.

تو خط 5 یک state داریم که اطلاعات دریافت شده از سرور رو داخلش ذخیره میکنیم. اگه با useState آشنا نیستین لطفا مقاله آموزش هوک useState در React رو مطالعه کنید.

تو خط 7 هم یک useEffect داریم که تابع موجود در این useEffect فقط یکبار در زمان اولین رندر اجرا میشه. این تابع لیست کاربران رو از سرور دریافت میکنه . اگه با هوک useEffect آشنا نیستید لطفا مقاله آموزش هوک useEffect در React رو مطالعه کنید.

حالا وارد فایل App.js در پوشه src بشید و کدش رو بصورت زیر تغییر بدید تا کامپوننت بالایی نمایش داده بشه :

import "./App.css";
import Home from "./components/Home";

function App() {
    return (
        <div className="App">
            <header className="App-header">
                <Home />// همون کامپوننتی که داخلش لیست کاربران رو نشون میدیم
            </header>
        </div>
    );
}

export default App;

تو تیکه کد بالا ، کامپوننت Home رو فراخوانی کردیم و نمایش دادیم .( همون کامپوننتی که داخلش لیست کاربران رو از سرور میگیریم و نمایش میدیم )

حالا اگه به آدرس http://localhost:3000 بریم ، باید کامپوننت Home که لیست کاربران مارو نشون میده، ببینیم:

خب تا اینجای کار عالیه ! تونستیم لیست کاربرانی که تو سرور ذخیره کردیم رو نشون بدیم.

حالا بیاید یه کاری کنیم ..

بیاید یک کاربر به لیست کاربران خودمون در سرور اضافه کنیم و ببینیم چه اتفاقی در فِرانت اِند میوفته .

از پوشه server فایل index.js رو باز کنید و یک کاربر به آرایه data اضافه کنید. مثلا کاربر زیر رو اضافه کنید :

...
const data = [
    ...
    {
        "name": "جعفر",
        "age": 65,
        "avatar": "https://frontendi.com/wp-content/uploads/2023/08/frontendi-logo-3.png"
    }
]
...

حالا که یک کاربر به لیست data ی خودمون اضافه کردیم ، به صفحه Home برمیگردیم. در کمال تعجب میبینیم که لیست کاربران ما آپدیت نشده و فقط درصورتی که صفحه رو refresh کنیم، لیست کاربران ما بروز میشه !!

تو مثال بالا چه اتفاقی افتاد ؟!

اطلاعات ما ( لیست کاربران ما ) در سرور تغییر کرد اما در صفحه ی وبسایت ما هیچ تغییری رخ نداد! تغییرات در صفحه وبسایت ما فقط با رفرش کردن صفحه صورت میگیره و این خیلی بده !

برخی وبسایت ها نیاز دارن که اطلاعات سرور رو بصورت زنده ( real-time ) به کاربر نمایش بدن .

اینجاست که کتابخانه SWR به ما کمک میکنه 🙂

دریافت اطلاعات از سرور با SWR

کتابخانه SWR در React عملیات re-validating رو بصورت خودکار انجام میده و اطلاعات جدید رو بصورت خودکار از سرور بصورت real-time دریافت میکنه تا دیگه نیاز نباشه صفحه رو refresh کنیم تا اطلاعات جدید رو از سرور بگریم.

برای دریافت لیست کاربران از سرور به کمک SWR ، باید به مسیر src/components/Home.js بریم و کامپوننت Home رو بصورت زیر تغییر بدیم :

import React from "react";
import useSWR from "swr";

const Home = () => {
    const fetcher = (...args) => fetch(...args).then((res) => res.json());
    const { data, error, isLoading } = useSWR("http://localhost:3001", fetcher);

    if (error) return <div>Failed to fetch users.</div>;
    if (isLoading) return <h2>Loading...</h2>;
    return (
        <>
            <div>
                {data.map((user, index) => {
                    return <h2 key={index}>{user.name}</h2>;
                })}
            </div>
        </>
    );
};

export default Home;

کد بالا نحوه و چگونگی استفاده از SWR و هوک SWR در ری اکت هست. بیاید ببینیم تو تیکه کد بالا چه اتفاقی افتاده ..

تو خط 2 ، هوک useSWR رو از کتابخوه swr فراخونی کردیم.

از هوک useSWR در خط 5 استفاده کردیم. این هوک از ما 2 آرگومان میگیره. اولین آرگومان یک key هست که ما آدرس API رو بهش دادیم ( همون API که بهمون لیست کاربران رو برمیگردونه )

دومین آرگومان یک تابع fetcher هست که عملیات دریافت داده ( fetching ) رو برامون انجام میده.

این تابع در خط 5 تعریف شده. ( این تابع همون key رو fetch میکنه و پاسخی که سرور بهمون داده رو با فرمت json برمیگردونه )

هوک useSWR سه مقدار به ما برمیگردونه.( در خط 6 )

اولین مقدار data هست. این مقدار یک Object هست که شامل اطلاعات دریافتی از سرور ( لیست کاربران ) هست.

دومین مقدار error هست. error فقط درصورتی که دریافت داده از سرور با خطا مواجه بشه، شامل اطلاعات ارور هست.

سومین مقدار isLoading هست. این مقدار یک مقدار Boolean هست و زمانیکه اطلاعات درحال دریافت هست مقدار True به خودش میگیره.

زمانیکه اطلاعات دریافت شد ، مقدار False به خودش میگیره. ( چون دریافت داده از سرور کمی طول میکشه )

از isLoading میتونیم برای نمایش حالت لطفا کمی صبر کنید، استفاده کنیم ( با آیکن خیلی قشنگتر میشه )

حالا اگه به صفحه Home بریم، باید لیست کاربران خودمون رو بصورت زیر ببینیم :

خب تا اینجای کار عالی پیش رفتیم 🙂

حالا بیاید یه کاربر جدید به لیست کاربران خودمون در سرور اضافه کنیم و ببینیم که آیا تغییرات بصورت real-time و در لحظه در صفحه وبسایت نمایش داده میشن یا نه!

دوباره به مسیر server/index.js میریم و یک کاربر به لیست کاربران قبلی خودمون اضافه میکنیم:

...
const data = [
    ...
    {
        "name": "فاطمه",
        "age": 21,
        "avatar": "https://frontendi.com/wp-content/uploads/2023/08/frontendi-logo-3.png"
    }
]
...

حالا اگه به صفحه Home برگردیم، میبینیم که بدون هیچ Refresh کردن صفحه ای ، اطلاعات جدید از سرور دریافت شده و در صفحه نمایش داده شده !!!

این قدرت SWR رو به ما نشون میده 🙂

به این نکته هم توجه کنید که وقتی اطلاعات ما در سرور آپدیت میشه ، چقدر سریع در وبسایت نمایش داده میشه و این تجربه کاربری خیلی خوبی رو به user های ما میده.

اتفاقی که در بالا افتاد بخاطر قابلیت Auto re-Validating در SWR بود.

اما کتابخانه SWR قابلیت های دیگه ای هم داره !

Pagination در کتابخانه SWR ( صفحه بندی )

کتابخانه SWR در ری اکت ، به ما قابلیت Pagination هم میده! ما باید برای اطلاعاتی که خیلی زیادی هستن و در یک صفحه جا نمیشن، Pagination قرار بدیم تا کاربر بتونه در هر صفحه تعداد محدودی اطلاعات مشاهده کنه.

تو مثال زیر به کمک کتابخانه SWR در React ، از قابلیت Pagination استفاده کردیم .

از یک API تستی میخوایم تعداد زیادی اطلاعات دریافت کنیم و با Pagination اونهارو صفحه بندی کنیم.

یک فایل به اسم Character.js در پوشه components بسازید و کد زیر رو داخلش قرار بدید :

import React from "react";
import { useState } from "react";
import useSWR from "swr";
import styled from "styled-components";

const Wrapper = styled.div`
    margin: 50px auto;
`;
const Character = styled.div`
    width: 250px;
    height: 200px;
    border-radius: 6px;
    outline: none;
    border: none;
`;

const Button = styled.button`
    width: 120px;
    height: 40px;
    border-radius: 6px;
    background-color: #6c6ce3;
    margin-inline: 10px;
    outline: none;
    border: none;
    color: white;
    font-weight: bold;
`;

const Container = styled.div`
    width: 100%;
    margin: 0 auto;
    display: grid;
    gap: 10px;
    padding-block: 20px;
    grid-template-columns: 2fr 2fr 2fr 2fr;
`;

const Characters = () => {
    const [pageIndex, setPageIndex] = useState(1);

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

    const { data, error, isLoading } = useSWR(
        `https://rickandmortyapi.com/api/character/?page=${pageIndex}`,
        fetcher,
    );

    if (error) return <div>Failed to fetch characters.</div>;
    if (isLoading) return <h2>Loading...</h2>;

    return (
        <>
            <Container>
                {data.results.map((character) => (
                    <Character key={character.id}>
                        <img width={100} height={100} src={character.image} />
                        <div>{character.name}</div>
                    </Character>
                ))}
            </Container>
            <Wrapper>
                <Button onClick={() => setPageIndex(pageIndex - 1)}>
                    Previous
                </Button>
                <Button onClick={() => setPageIndex(pageIndex + 1)}>
                    Next
                </Button>
            </Wrapper>
        </>
    );
};

export default Characters;

تو مثال بالا ما از یک سرویس API برای دریافت اطلاعات تستی استفاده کردیم. ( خط 44 )

این API به ما در هر درخواست 20 اطلاعات برمیگردونه. حالا برای اینکه کامپوننت Character رو بتونیم مشاهده کنیم باید در فایل App.js فراخوانیش کنیم :

import "./App.css";
// import Home from './components/Home'
import Characters from "./components/Characters";

function App() {
    return (
        <div className="App">
            <header className="App-header">
                {/* <Home/> */}
                <Characters />
            </header>
        </div>
    );
}

export default App;

حالا اگه به صفحه اصلی وبسایتمون بریم ، باید لیست کاراکتر های دریافت شده از سرور رو داخلش صفحه ببنیم. دو Button در پایین صفحه داریم که به کمک اونها میتونیم به صفحه قبل و بعد بریم.

نکته جالب اینجاست که در هر صفحه فقط تعداد محدودی اطلاعات میتونیم مشاهده کنیم.

پس خیلی راحت تونستیم Pagination رو به کمک SWR پیاده سازی کنیم.

لطفا برای مطالعه ادامه این مقاله + مثالها و تیکه کدهای بیشتر روی لینک زیر کلیک کنید :

SWR در ری اکت