**در این سؤال، از کتابخانهی `react-router-dom@6` استفاده شده است.**
---
![ظاهر برنامه](https://quera.org/qbox/view/RYLyFlB1MJ/useFilter_DK.gif)
در دیجیکالا تعداد زیادی داده وجود دارد که برخی اوقات لازم است آنها را فیلتر کنیم. حال، میخوایم راهی پیدا کنیم که این فیلتر اعمالشده را به راحتی با بقیه به اشتراک بگذاریم. برای این کار، تصمیم گرفتهایم یک هوک بنویسیم که کارش هماهنگ کردن و اتصال *state* فرم فیلتر با آدرس صفحه است. بنابراین شما باید در کامل کردن این هوک (که آن را `useFilter` نامیدهایم) به ما کمک کنید.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/problemset/assignments/4367/download_problem_initial_project/136479/) دانلود کنید.
<details class="green">
<summary>
ساختار فایلهای پروژه
</summary>
```
useFilter
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── assets
│ │ ├── css
│ │ │ └── index.css
│ │ └── font
│ │ └── Vazir.ttf
│ ├── components
│ │ ├── chips
│ │ │ ├── chips.css
│ │ │ └── index.jsx
│ │ ├── formInput
│ │ │ ├── Checkbox
│ │ │ │ ├── Checkbox.css
│ │ │ │ └── index.jsx
│ │ │ ├── CheckboxGroup
│ │ │ │ ├── CheckboxGroup.css
│ │ │ │ └── index.jsx
│ │ │ ├── Dropdown
│ │ │ │ └── index.jsx
│ │ │ ├── RangeInput
│ │ │ │ ├── RangeInput.css
│ │ │ │ └── index.jsx
│ │ │ ├── TextInput
│ │ │ │ └── index.jsx
│ │ │ ├── Textarea
│ │ │ │ └── index.jsx
│ │ │ ├── FilterForm.jsx
│ │ │ ├── FormClear.jsx
│ │ │ └── FormItem.jsx
│ │ └── Location.jsx
│ ├── constants
│ │ └── FormType.js
│ ├── data
│ │ └── formData.js
│ ├── hooks
│ │ └── useFilter.js
│ ├── App.jsx
│ └── index.jsx
└── package.json
```
</details>
<details class="brown">
<summary>
راهاندازی پروژه
</summary>
برای اجرای پروژه، باید *Node.js* و *npm* را از قبل نصب کرده باشید.
- پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
- در پوشهی `useFilter`، دستور `npm install` را برای نصب نیازمندیها اجرا کنید.
- در همین پوشه، دستور `npm start` را برای راهاندازی پروژه اجرا کنید.
</details>
دادهی لازم برای ساخت فرم بهصورت یک آرایه به هوک `useFilter` پاس داده میشود. هر عضو این آرایه معادل یک المان (*input element*) از فرم است. آبجکت زیر شامل تمامی *prop* ها است که به المانها داده میشود. بعضی از این *prop* ها مربوط به نوع خاصی از المانها هستند. برای مثال، `options` فقط مربوط به `dropdown` و `checkbox group` است.
```json
{
"type": "text"|"number"|"dropdown"|"textarea"|"checkbox"|"checkbox-group",
"name": string,
"label": string,
"children"?: string[],
"parent"?: string,
"options": [{ value: string|number, title: string|number }],
}
```
### فیلد `options`
همانطور که گفته شد، این فیلد مختص `dropdown` و `checkbox group` است.
### فیلد `parent` و `children`
بعضی از المانهای فرم ممکن است دارای وابستگی به المان دیگری باشند. این وابستگی در فرزند با فیلدی به نام `parent` که یک رشته شامل نام پدر است، نمایش داده میشود و در المان پدر با فیلدی به نام `children` که شامل آرایهای از رشتهها شامل نام (فیلد `name`) فرزندان است.
### اتصال (*binding*) دوطرفه
هوک خواستهشده باید یک اتصال (*binding*) دوطرفه بین پارامترهای جستجوی آدرس (*query params*) و فیلدهای فرم داشته باشد، به این معنی که با هر تغییر در المانهای فرم، بلافاصله آدرس به روزرسانی شود و برعکس. این هوک باید یک آبجکت شامل کلیدهای زیر برگرداند:
```json
{
"filterState": {
"name": value
}
"setFilterState": (filterState)=>void
"onChange": (e, name, type)=>void
"onClear": (name)=>void
"onClearAll": ()=>void
}
```
آرایهای که فرم از روی آن ساخته میشود (متغیر `formData`) بهعنوان ورودی به هوک داده میشود.
### متغیر `filterState` و تابع `setFilterState`
این دو مورد، عملکردی معادل متغیر و تابع از هوک معروف `useState` را دارند. متغیر یک آبجکت است که هر کلید آن باید برابر با نام (فیلد `name`) از المان فرم باشد و مقدار آن هم مقدار آن المان است (فیلد `value`).
### تابع `onChange`
این تابع هنگام تغییر هر المان فرم صدا زده میشود.
### تابع `onClear`
در پایین صفحه، دکمههایی برای پاک کردن مقادیر المانهای فرم وجود دارد. با کلیک کردن بر روی این دکمهها، باید مقدار المان مورد نظر از فرم پاک شود و همزمان این مقدار از آدرس هم پاک شود. این تابع به عنوان ورودی نام (فیلد `name`) المان موردنظر را دریافت میکند.
**نکته:** در صورتی که مقدار المان پدر پاک شود و یا تغییر کند، مقدار المان فرزند حتماً باید ریست شود، یعنی هم از آدرس پاک شود و هم مقدار `input` مربوطه از فرم خالی شود.
### تابع `onClearAll`
در صورتی که روی دکمهی پاک کردن همه کلیک شود، مسلماً باید تمامی مقادیر المانها پاک شود و آدرس به حالت `/` برود.
### تجزیه و ترکیب آدرس (*url parser & url stringify*)
همانطور که میدانید، در حالت کلی پارامترهای جستجوی آدرس (*query parameter*) بهصورت زیر ساخته میشوند:
?name1=value1&name2=value2&name3=v1,v2,v3
اما به دلایلی نیاز داریم از فرمت متفاوتی برای ساخت این آدرس استفاده کنیم؛ به اینصورت که برای پشت سر هم قرار دادن پارامترها به جای کارکتر`&` از کارکتر`+`و برای جدا کردن *key* و *value* بهجای کارکتر`=` از `~` استفاده میکنیم. همچنین در مواردی که یک آرایه از مقادیر داریم (این اتفاق فقط در المان `checkbox group` رخ میدهد)، برای جدا کردن آنها از هم به جای `,` از کارکتر `--` استفاده میکنیم (ترتیب مقادیر مهم نیست). برای مثال، این پارامترهای رایج آدرس:
?name1=value1&name2=v1,v2,v3
به این صورت تبدیل میشوند:
?name1~value1+name2~v1--v2--v3
و در صورت تجزیه (*parse*) این پارامترها به آبجکت زیر میرسیم:
```json
{
"name1": "value1",
"name2": ["v1", "v2", "v3"]
}
```
# نکات
- فیلدی که مقداری ندارد **نباید** در پارامترهای جستجو (*query params*) آدرس ظاهر شود.
- در صورتی که روی دکمههای پاک کردن (*clear*) پایین صفحه کلیک شود، باید پارامتر موردنظر از آدرس مربوطه پاک شود و همزمان مقدار `input` متصل به آن فیلد پاک شود.
- در صورتی که یک فیلد دارای `children` پاک شود، در اینصورت باید تمامی فرزندانش نیز از آدرس پاک شوند.
- فیلدهای فیلتر بایستی مقداردهی اولیه شوند، بهطوری که در زمان بارگذاری صفحه، در صورتی که آدرس دارای پارامترهای جستجو (*query params*) باشد، باید بلافاصله این مقادیر با فرم هماهنگ شوند و فیلدهای فرم با مقادیر آدرس پر شوند.
- شما تنها مجاز به اعمال تغییرات در فایل `hooks/useFilter.js` هستید.
- تنها مجاز به استفاده از کتابخانههای ذکر شده در صورت سوال هستید.
# آنچه باید آپلود کنید
پس از پیادهسازی موارد خواستهشده، فایل `useFilter.js` را آپلود کنید.