در چشم انداز همیشه در حال تحول توسعه وب، URL به عنوان یک قهرمان خاموش ظاهر شده است. همانطور که توسعه‌دهندگان فرانت‌اند چالش‌های ادغام یک state manager در سرور کامپوننت را بررسی می‌کنند، URL یک راه حل تازه در توسعه Next.js ارائه می‌دهد.

URL فراتر از نقش سنتی خود به عنوان یک آدرس وب، به یک ابزار مدیریت state قدرتمند تبدیل شده است که نحوه تعامل کاربران و ذخیره تجربیات آنلاین آن‌ها را متحول می‌کند.

فعالیت‌هایی که در زیر داریم کمک می‌کند تا درک کنیم که URL چگونه می‌تواند چنین کارهایی را در پروژه‌های Next.js انجام دهد و به کاربران و توسعه‌دهندگان کمک کند:

در ادامه قصد داریم تا این مفهوم را با یک مثال عملی در یک سرور کامپوننت بررسی نماییم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import Link from 'next/link';
export default function ProductPage({
searchParams,
}: {
searchParams: { [key: string]: string | string[] | undefined };
}) {
const selectedColor = (searchParams?.color || 'blue') as string;
const selectedSize = (searchParams?.size || 'M') as string;
const sizeVariants = ['S', 'M', 'L', 'XL'];
return (
// ...
<div>
{sizeVariants.map((size, index) => (
<Link
key={index}
href={`?${new URLSearchParams({
color: selectedColor,
size,
})}`}
className={`${
selectedSize === size ? 'bg-blue-500' : 'bg-gray-200'
} mr-2 inline-block h-8 w-8 text-center leading-8`}
>
{size}
</Link>
))}
</div>
// ...
);
}
import Link from 'next/link'; export default function ProductPage({ searchParams, }: { searchParams: { [key: string]: string | string[] | undefined }; }) { const selectedColor = (searchParams?.color || 'blue') as string; const selectedSize = (searchParams?.size || 'M') as string; const sizeVariants = ['S', 'M', 'L', 'XL']; return ( // ... <div> {sizeVariants.map((size, index) => ( <Link key={index} href={`?${new URLSearchParams({ color: selectedColor, size, })}`} className={`${ selectedSize === size ? 'bg-blue-500' : 'bg-gray-200' } mr-2 inline-block h-8 w-8 text-center leading-8`} > {size} </Link> ))} </div> // ... ); }
import Link from 'next/link';

export default function ProductPage({
  searchParams,
}: {
  searchParams: { [key: string]: string | string[] | undefined };
}) {
  const selectedColor = (searchParams?.color || 'blue') as string;
  const selectedSize = (searchParams?.size || 'M') as string;

  const sizeVariants = ['S', 'M', 'L', 'XL'];

  return (
    // ...
    <div>
      {sizeVariants.map((size, index) => (
        <Link
          key={index}
          href={`?${new URLSearchParams({
            color: selectedColor,
            size,
          })}`}
          className={`${
            selectedSize === size ? 'bg-blue-500' : 'bg-gray-200'
          } mr-2 inline-block h-8 w-8 text-center leading-8`}
        >
          {size}
        </Link>
      ))}
    </div>
    // ...
  );
}

این کامپوننت یک prop به نام

searchParams
searchParamsرا می‌پذیرد، که یک آبجکت حاوی جفت‌های key-value می‌باشد و پارامترهای query string مربوط به URL را نشان می‌دهد. متغیرهای
SelectColor
SelectColorو
SelectSize
SelectSizeاز این پارامترها استخراج می‌شوند.

فرض کنید یک صفحه محصول برای یک پیراهن صورتی در سایز “XL” داریم. URL آن ممکن است شبیه به مثال زیر باشد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
https://www.examplestore.com/product-page?color=pink&size=XL
https://www.examplestore.com/product-page?color=pink&size=XL
https://www.examplestore.com/product-page?color=pink&size=XL

اکنون می‌خواهیم نحوه کمک کد به این موضوع را بررسی کنیم.

مقادیر پیش‌فرض:

در اصل، مقادیر پیش‌فرض به‌عنوان یک شبکه ایمنی عمل می‌کنند و تضمین می‌کنند که کامپوننت به خوبی سناریوهای مختلف را مدیریت می‌کند و در مواجهه با بی‌نظمی‌های احتمالی در داده‌هایی که دریافت می‌کند، پرقدرت باقی می‌ماند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const selectedColor = (searchParams?.color || 'blue') as string;
const selectedSize = (searchParams?.size || 'M') as string;
const selectedColor = (searchParams?.color || 'blue') as string; const selectedSize = (searchParams?.size || 'M') as string;
const selectedColor = (searchParams?.color || 'blue') as string;
const selectedSize = (searchParams?.size || 'M') as string;

تولید لینک:

این خط ویژگی

href
hrefرا برای هر Link ایجاد می‌کند و یک آبجکت
URLSearchParams
URLSearchParamsجدید با رنگ انتخاب شده و اندازه خاص ایجاد می‌نماید.

با ترکیب

new URLSearchParams
new URLSearchParams، توسعه‌دهندگان می‌توانند URLهایی ایجاد کنند که استانداردها را رعایت کرده و از سازگاری در مرورگرهای مختلف اطمینان حاصل نمایند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
href={`?${new URLSearchParams({ color: selectedColor, size })}`}
href={`?${new URLSearchParams({ color: selectedColor, size })}`}
href={`?${new URLSearchParams({ color: selectedColor, size })}`}

استایل‌دهی:

classNameها به صورت پویا به Linkها استایل می‌دهند. به این صورت که سایز مورد نظر را با پس‌زمینه آبی برجسته می‌کنند و در نتیجه یک نشانه بصری ارائه می‌دهند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
className={`${
selectedSize === size ? 'bg-blue-500' : 'bg-gray-200'
} mr-2 inline-block h-8 w-8 text-center leading-8`}
className={`${ selectedSize === size ? 'bg-blue-500' : 'bg-gray-200' } mr-2 inline-block h-8 w-8 text-center leading-8`}
className={`${
  selectedSize === size ? 'bg-blue-500' : 'bg-gray-200'
} mr-2 inline-block h-8 w-8 text-center leading-8`}

در ادامه قصد داریم تا مثالی را در سمت کلاینت که دارای ورودی جستجو است بررسی کنیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
'use client';
import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { useSearchParams, usePathname, useRouter } from 'next/navigation';
export default function Search() {
const searchParams = useSearchParams();
const pathname = usePathname();
const { replace } = useRouter();
function handleSearch(term: string) {
const params = new URLSearchParams(searchParams);
if (term) {
params.set('query', term);
} else {
params.delete('query');
}
replace(`${pathname}?${params.toString()}`);
}
}
return (
<div className="relative flex flex-1 flex-shrink-0">
<label htmlFor="search" className="sr-only">
Search
</label>
<input
className="peer block w-full rounded-md border border-gray-200 py-[9px] pl-10 text-sm outline-2 placeholder:text-gray-500"
placeholder={placeholder}
defaultValue={searchParams.get('query')?.toString()}
onChange={(e) => {
handleSearch(e.target.value);
}}
/>
<MagnifyingGlassIcon className="absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500 peer-focus:text-gray-900" />
</div>
);
}
'use client'; import { MagnifyingGlassIcon } from '@heroicons/react/24/outline'; import { useSearchParams, usePathname, useRouter } from 'next/navigation'; export default function Search() { const searchParams = useSearchParams(); const pathname = usePathname(); const { replace } = useRouter(); function handleSearch(term: string) { const params = new URLSearchParams(searchParams); if (term) { params.set('query', term); } else { params.delete('query'); } replace(`${pathname}?${params.toString()}`); } } return ( <div className="relative flex flex-1 flex-shrink-0"> <label htmlFor="search" className="sr-only"> Search </label> <input className="peer block w-full rounded-md border border-gray-200 py-[9px] pl-10 text-sm outline-2 placeholder:text-gray-500" placeholder={placeholder} defaultValue={searchParams.get('query')?.toString()} onChange={(e) => { handleSearch(e.target.value); }} /> <MagnifyingGlassIcon className="absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500 peer-focus:text-gray-900" /> </div> ); }
'use client';

import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { useSearchParams, usePathname, useRouter } from 'next/navigation';
 
export default function Search() {
  const searchParams = useSearchParams();
  const pathname = usePathname();
  const { replace } = useRouter();
 
  function handleSearch(term: string) {
    const params = new URLSearchParams(searchParams);
    if (term) {
      params.set('query', term);
    } else {
      params.delete('query');
    }
    replace(`${pathname}?${params.toString()}`);
  }
}


return (
    <div className="relative flex flex-1 flex-shrink-0">
      <label htmlFor="search" className="sr-only">
        Search
      </label>
      <input
        className="peer block w-full rounded-md border border-gray-200 py-[9px] pl-10 text-sm outline-2 placeholder:text-gray-500"
        placeholder={placeholder}
        defaultValue={searchParams.get('query')?.toString()}
        onChange={(e) => {
          handleSearch(e.target.value);
        }}
      />
      <MagnifyingGlassIcon className="absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500 peer-focus:text-gray-900" />
    </div>
  );
}

قطعه کدی که داریم از هوک‌های navigation سمت کلاینت نسخه Next.js 14 استفاده می‌نماید.

useSearchParams
useSearchParamsپارامترهای URL فعلی را بازیابی می‌کند،
usePathname
usePathnameنام مسیر فعلی را دریافت می‌کند و
userRouter
userRouterفانکشنالیتی‌های مسیریابی را ارائه می‌دهد. حتی اگر در سمت کلاینت هستیم و می‌توانیم از هوک‌هایی مانند useState استفاده کنیم، اما ما به دلیل مزایایی که استفاده از URL به عنوان state manager ارائه می‌دهد به کار خود ادامه می‌دهیم.

handleSearch:

تابع

handleSearch
handleSearchپارامترهای URL را بر اساس عبارت جستجوی ارائه شده، به روز رسانی می‌کند. این تابع از یک آبجکت
URLSearchParams
URLSearchParamsبرای مدیریت پارامترها، تنظیم یا حذف پارامتر query بر اساس اینکه آیا یک عبارت ارائه شده است یا خیر، استفاده می‌کند. این تابع در نهایت، از
replace
replaceبرای به روز رسانی URL استفاده می‌نماید.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function handleSearch(term: string) {
const params = new URLSearchParams(searchParams);
if (term) {
params.set('query', term);
} else {
params.delete('query');
}
replace(`${pathname}?${params.toString()}`);
}
function handleSearch(term: string) { const params = new URLSearchParams(searchParams); if (term) { params.set('query', term); } else { params.delete('query'); } replace(`${pathname}?${params.toString()}`); }
function handleSearch(term: string) {
    const params = new URLSearchParams(searchParams);
    if (term) {
      params.set('query', term);
    } else {
      params.delete('query');
    }
    replace(`${pathname}?${params.toString()}`);
  }

defaultValue:

searchParams.get('query')?.toString()
searchParams.get('query')?.toString() مقدار پارامتر query را از URL بازیابی می‌کند. اگر پارامتر وجود داشته باشد، مقدار آن را برمی‌گرداند. در غیر این صورت،
null
nullرا return می‌کند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<input
className="peer block w-full rounded-md border border-gray-200 py-[9px] pl-10 text-sm outline-2 placeholder:text-gray-500"
placeholder={placeholder}
defaultValue={searchParams.get('query')?.toString()}
onChange={(e) => {
handleSearch(e.target.value);
}}
/>
<input className="peer block w-full rounded-md border border-gray-200 py-[9px] pl-10 text-sm outline-2 placeholder:text-gray-500" placeholder={placeholder} defaultValue={searchParams.get('query')?.toString()} onChange={(e) => { handleSearch(e.target.value); }} />
<input
        className="peer block w-full rounded-md border border-gray-200 py-[9px] pl-10 text-sm outline-2 placeholder:text-gray-500"
        placeholder={placeholder}
        defaultValue={searchParams.get('query')?.toString()}
        onChange={(e) => {
          handleSearch(e.target.value);
        }}
      />

اکنون به اشتراک گذاری راحت تجربیات مورد نظر کاربران را بررسی خواهیم کرد.

یکی از قانع‌کننده‌ترین جنبه‌های استفاده از URL به‌عنوان state manager، توانایی یکپارچه آن برای به اشتراک گذاشتن تجربیات مورد نظر کاربران است. کاربری را تصور کنید که در حال بررسی مجموعه‌ای از محصولات صورتی رنگ با سایز XL فروشگاه است. هنگام استفاده از URL به عنوان state manager، تجربیات مورد نظر کاربران پویا شده و به راحتی قابل اشتراک گذاری می‌باشد.

برای مثال، آدرس اینترنتی را در نظر بگیرید:

https://www.examplestore.com/product-page?color=pink&size=XL
https://www.examplestore.com/product-page?color=pink&size=XL. این لینک یک حالت خاص را در برنامه بیان می‌کند که در این مورد، محصولاتی را به نمایش می‌گذارد که مطابق با ترجیح کاربر، دارای رنگ صورتی و اندازه XL هستند. اتفاق خاص زمانی رخ می‌دهد که کاربر این لینک را به اشتراک بگذارد.

در ادامه فهرستی از نمونه‌هایی وجود دارد که نشان می‌دهد چگونه URL می‌تواند به عنوان یک state manager ایده‌آل، به‌ویژه در زمینه سرور کامپوننت‌ها عمل کند:

جمع‌بندی

در این مقاله سعی کردیم تا بررسی کنیم که چرا URL می‌تواند به عنوان یک state manager ایده‌آل در سرور کامپوننت‌ها و کلاینت کامپوننت‌های پروژه‌های Next.js مورد استفاده قرار بگیرد. موارد استفاده زیادی در این مورد وجود دارد که ما فقط به چند نمونه از آن‌ها اشاره کردیم.