ریکت
21 مرداد 1402
داکیومنت ریکت - قسمت دوم
سلام دوستان امیدوارم خوب باشید و همیشه درحال یادگیری🧑💻. توی دومین قسمت از داکیومنت ریکت میخوایم تفکر ریکت ایی پیدا کنیم.
ریکت میتونه تفکر شما رو زمانی که به یک طرح نگاه میکنید تغییر بده. وقتی که یک طرح داریم و میخوایم اونو با ریکت توسعه بدیم، اول میاییم اونو به کامپوننت های کوچک تر تقسیم میکنیم، بعدش میام هر کامپوننت رو جدا توسعه میدیم، درنهایت کامپوننت ها رو به هم وصل میکنیم و اگه لازم باشه ارتباط دیتا رو بین کامپوننت ها برقرار میکنیم.
فرض کنید ساختار داده ایی که داریم بصورت زیر است:
[
{ category: "Fruits", price: "$1", stocked: true, name: "Apple" },
{ category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit" },
{ category: "Fruits", price: "$2", stocked: false, name: "Passionfruit" },
{ category: "Vegetables", price: "$2", stocked: true, name: "Spinach" },
{ category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin" },
{ category: "Vegetables", price: "$1", stocked: true, name: "Peas" }
]
و طرحی که داریم هم به این شکل هست:
برای پیاده سازی لازم هست 5 قدم رو انجام بدیم.
قدم 1: تقسیم طرح به سلسله مراتبی از کامپوننتها
توی تقسیم کردن طرح به کامپوننت های کوچک تر میتونید از قانون single responsibility principle استفاده کنید، یعنی هر کامپوننت یک کار رو انجام بده. اگه یک کامپوننت هم شروع کرد به بزرگ شدن باز اون رو هم به کامپوننت های کوچکتر که زیر مجموعه اون هستن تقسیم میکنیم.
بطور مثال داخل طرحی که داریم میتونیم به این شکلی تقسیم کنیم:
- کامپوننت
FilterableProductTable
در واقع والد اصلی ما هست که تمام برنامه توی اون قرار میگیره. - کامپوننت
SearchBar
قسمت سرچ برنامه هست. - کامپوننت
ProductTable
والد برای بخش دیتامون هست. - کامپوننت
ProductCategoryRow
هدر های هر بخش دسته بندی هست که دسته بندی های مختلف رو از هم جدا میکنه. - در نهایت کامپوننت
ProductRow
که برای هر سطر از دیتا جدول ما استفاده میشه.
در واقع ساختار کامپوننت هامون به این شکل میشه که، یک کامپوننت والد داریم(که شامل کل برنامه هست) به نام FilterableProductTable
که دو تا فرزند SearchBar
و ProductTable
داره و خود ProductTable
والد دو تا کامپوننت ProductCategoryRow
و ProductRow
هست.
*FilterableProductTable
*SearchBar
*ProductTable
*ProductCategoryRow
*ProductRow
قدم 2: یک نسخه استاتیک درست کنیم
خوب حالا وقت اینه که کامپوننت هامون رو بنویسیم. راه ساده اینه که اول UI و طرح کلی رو پیاده سازی کنیم یا اصطلاحا static version بسازیم و بعدش تعامل (کار کردن با state ها و ...) رو بهش اضافه کنیم. ساختن static version بیشتر نیاز به تایپ کردن داره و اضافه کردن تعامل بیشتر نیاز به فکر کردن.
وقتی که اومدیم کامپوننت هامون رو نوشتیم برای اینکه دیتا ها رو به کامپوننت هامون پاس بدیم از props استفاده میکنیم. در واقع props کمک میکنه که دیتا از والد به فرزند برسه. اگه با مفهوم state آشنا هستین، ما از state ها داخل static version استفاده نمیکنیم. state ها برای دیتا هایی مناسب هستن که در زمان ممکنه تغییر کنند و زمانی که ما میاییم static version میسازیم اصلا نیاز به state نداریم.
همچنین برای توسعه کامپوننت ها دو حالت وجود داره یا بیاییم از بالا برنامه که اینجا FilterableProductTable
هست شروع کنیم به توسعه و همینطوری پیش بریم تا کامپوننت های فرزند(top down)، و یا از پایین ترین نقطه کامپوننت که اینجا ProductRow
هست شروع کنیم بریم به والد برسیم(bottom up). توی پروژه های بزرگ تر پیشنهاد میشه از bottom-up استفاده کنید.
function ProductCategoryRow({ category }) {
return (
<tr>
<th colSpan="2">
{category}
</th>
</tr>
);
}
function ProductRow({ product }) {
const name = product.stocked ? product.name :
<span style={{ color: 'red' }}>
{product.name}
</span>;
return (
<tr>
<td>{name}</td>
<td>{product.price}</td>
</tr>
);
}
function ProductTable({ products }) {
const rows = [];
let lastCategory = null;
products.forEach((product) => {
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category} />
);
}
rows.push(
<ProductRow
product={product}
key={product.name} />
);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
function SearchBar() {
return (
<form>
<input type="text" placeholder="Search..." />
<label>
<input type="checkbox" />
{' '}
Only show products in stock
</label>
</form>
);
}
function FilterableProductTable({ products }) {
return (
<div>
<SearchBar />
<ProductTable products={products} />
</div>
);
}
const PRODUCTS = [
{category: "Fruits", price: "$1", stocked: true, name: "Apple"},
{category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"},
{category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"},
{category: "Vegetables", price: "$2", stocked: true, name: "Spinach"},
{category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"},
{category: "Vegetables", price: "$1", stocked: true, name: "Peas"}
];
export default function App() {
return <FilterableProductTable products={PRODUCTS} />;
}
قدم 3: استفاده کامل و حداقلی از استیت ها
خوب حالا زمان اینه که state به برنامه اضافه کنیم تا کاربر بتونه تعامل داشته باشه. مهمترین اصل زمان استفاده از state ها DRY (Don’t Repeat Yourself) هستش. باید حداقل استفاده از state ها رو داخل اپ داشته باشیم. به طور مثال فرض کنید شما میخواید یک لیست محصولات رو نشون بدید، خوب اونا رو داخل یک state ذخیره میکنید. حالا اگه بخوایین تعداد این آیتم ها رو بدونید دیگه لازم نیست یک state جدید تعریف کنید فقط کافیه طول همون آرایه state قبلی رو بخونید. خوب حالا داخل همین پروژه خودمون این چهار نوع دیتا رو داریم:
- لیست اصلی محصولات
- مقدار سرچ ایی که کاربر انجام میده
- مقدار checkbox
- لیست فیلتر شده
خوب حالا کدوم یک از این دیتا ها رو میشه به عنوان یک state تعریف کرد؟ چند تا سوال از خودمون میپرسیم.
- آیا در طول زمان بدون تغییر میمونه؟ اگه آره این state نیست.
- آیا از والد به فرزند ارسال شده ؟ اگه آره این state نیست.
- آیا میتونیم جز state و یا props های داخل کامپوننت حساب کنیم؟ اگه آره قطعا state نیست.
اگه موارد بالا رو خیر باشن قطعا جز state ها حساب میشه.
خوب حالا بریم سراغ اون 4 جریان دیتا ایی که داخل برنامه داشتیم و ببینیم اینا state هستن یا نه؟
- لیست اصلی محصولات چون از prop میاد پس state نیست.
- مقدار سرچ ایی که کاربر انجام میده یک state هست چون در گذر زمان عوض میشه.
- مقدار checkbox هم همینطور در گذر زمان عوض میشه پس یک state هست.
- لیست فیلتر شده یک state نیست چون ما لیست اصلی محصولات رو که از prop میاد رو داریم با همون prop میتونیم فیلتر رو انجام بدیم و نیاز به state جدید نیست.
قدم 4: استیت ما کجا باید ایجاد بشه؟
حالا نوبت این هست که مشخص کنیم این استیت ایی که داریم، کدوم کامپوننت وظیفه تغییر استیت یا مالکیت اون رو داره؟
به این نکته دقت کنید که جریان داده ریکت one-way
هست یعنی از دیتا از والد بصورت سلسله مراتبی به فرزندان میرسه. به همین خاطر ممکن هست تشخیص اینکه کدوم کامپوننت مالک استیت هست براتون سخت باشه، به همین خاطر این مراحل رو میتونید پیش برید:
- تمام کامپوننت هایی که دارن براساس استیت آیتمی رو رندر میکنند رو تشخیص بدیم.
- نزدیک ترین والد مشترک بینشون رو پیدا کنیم.
- در نهایت تشخیص میدیم استیت ما کجا باشه.
استیت ما میتونه داخل نزدیک ترین والد باشه، یا میتونه داخل کامپوننت های بالاتر از نزدیک ترین والد باشه. اگر هم نتونستید جای مناسب برای استیت پیدا کنید یک کامپوننت ایجاد کنید و داخل اون استیت رو قرار بدین و توی سلسله مراتب بالاتر از نزدیک ترین والد قرار بدین.
بطور مثال در پروژه ایی که داریم سه گام بصورت زیر میشه:
- مشخص کنیم کدوم کامپوننت داره از state ما استفاده میکنه. در پروژه ما
ProductTable
وSearchBar
هستش. - قدم بعد باید والد مشترک اینا رو پیدا کنیم که
FilterableProductTable
هستش. - در آخر تصمیم میگیرم کجا state ما باید باشه که در
FilterableProductTable
قرار میگیره.
همونطور که میدونید state ها رو با کمک هوک useState
میسازیم و بعدش بصورت prop به کامپوننت هامون میدیم.
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
بعد از filterText
و inStockOnly
رو به کامپوننت های ProductTable
و SearchBar
پاس میدیم.
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly} />
<ProductTable
products={products}
filterText={filterText}
inStockOnly={inStockOnly} />
</div>
قدم 5: ایجاد جریان داده معکوس
تا الان اپ ما همه چیز هاش کامل شده و کار میکنه فقط این موضوع مونده که چطوری مقدار inStockOnly
و filterText
رو باید عوض کنیم تا سرچ انجام بشه؟
اینجا از مفهومی به نام inverse data flow
استفاده میکنیم. الان مالک state های filterText
و inStockOnly
کامپوننت FilterableProductTable
هستش حالا میخوایم این اجازه رو داشته باشیم که کامپوننت SearchBar
بیاد اینا رو تغییر بده باید چیکار کنیم؟
باید فانکشن set
جفت state
ها رو بصورت prop
به SearchBar
بدیم و اونجا میایم اونو صدا میزنیم تا state
ما آپدیت بشه.
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
return (
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly}
onFilterTextChange={setFilterText}
onInStockOnlyChange={setInStockOnly} />
و داخل کامپوننت SearchBar
میایم تابع set
رو به onChange
فیلدی که داریم پاس میدیم.
خوب بچه ها امیدوارم این قسمت براتون مفید بوده باشه. خوشحال میشم اگه انتقادی یا پیشنهادی دارید برام بنویسید تا محتوای بهتری تولید بشه ❤️
منبع:
این پست برات مفید بود؟
0
0
0
0
نظرات