تا این قسمت خوب پیش رفته ایم اما هیچ صفحه ای برای نمایش سفارشات کاربران نداریم. بنابراین به پوشه ی containers رفته و پوشه ی جدیدی به نام orders ایجاد کنید که حاوی فایلی به نام Orders.js باشد. مثل همیشه با کد اولیه ی زیر شروع می کنیم:
import React, { Component } from 'react'; class Orders extends Component { render() { return (); } } export default Orders;
یک کامپوننت ساده ی کلاس-محور که دارای متد render بوده و قرار است مقداری JSX را return کند. سوال اصلی این است که چه چیزی را باید render کنیم؟ ما می خواهیم سفارشات کاربر را در این قسمت نمایش دهیم بنابراین باید کامپوننتی جداگانه بسازیم که مخصوص هر یک سفارش باشد. برای این کار وارد پوشه ی Order شوید (حواستان باشد که آن را با orders اشتباه نگیرید – درون پوشه ی components) و دو فایل Order.js و Order.module.css را در آن بسازید.
من میخواهم کامپوننت Order.js را به صورت یک کامپوننت کاربردی (تابع) ایجاد کنم بنابراین:
import React from 'react'; const order = (props) => (); export default order;
ما می خواهیم هر کدام از سفارشات را به صورت یک div داشته باشیم بنابراین عنصر ریشه ای را div انتخاب می کنیم. همچنین فرض کنید می خواهیم محتویات همبرگر و قیمت آن را درون این div داشته باشیم بنابراین می گوییم:
import React from 'react'; const order = (props) => ( <div> <p>Ingredients: Salad (1)</p> <p>Price: <strong>USD 5.45</strong></p> </div> ); export default order;
این شکل کلی کار ما است. طبیعتا این کد ها واقعی نیست و بعدا آن را به صورت صحیح می نویسیم اما فعلا می خواهم با کلیت کار آشنا شوید (محتویات + قیمت). حالا نوبت این است که کد بالا را استایل دهی کنیم بنابراین وارد فایل Order.module.css شده و کد های CSS زیر را در آن کپی کنید (البته می توانید خودتان با سلیقه ی خودتان استایل دهی کنید):
.Order { width: 100%; border: 1px solid #eee; box-shadow: 0 2px 3px #ccc; padding: 10px; margin: 10px auto; box-sizing: border-box; }
حالا به Order.js برمیگردیم تا این کلاس را روی div اعمال کنیم:
import classes from './Order.css';
سپس به راحتی آن را به div می دهیم:
const order = (props) => ( <div className={classes.Order}> <p>Ingredients: Salad (1)</p> <p>Price: <strong>USD 5.45</strong></p> </div> );
این یک کامپوننت ساده برای هر سفارش است که بعدا کد آن را کامل تر می کنیم اما فعلا قرار است درون Orders.js به تعداد سفارشات، این کامپوننت ها را نمایش دهیم. مشخص است که باید این سفارشات را از پایگاه داده بگیریم بنابراین به Firebase رفته و تمام قسمت orders را پاک کنید. اگر دقت کرده باشید در جلسات قبلی مرتبا پروسه ی سفارش همبرگر را تست می کردیم به همین دلیل سفارشات زیادی را در Firebase ثبت کرده ایم. من می خواهم شروع کارمان تمیز باشد، همچنین به ازای تمام این سفارشات کامپوننت نمایش ندهیم بنابراین تمام قسمت orders را حذف کنید:
سپس مقدار meat را روی صفر بگذارید تا با یک همبرگر خالی شروع کنیم.
در قدم بعدی وارد Orders.js شده و کامپوننت Order.js را وارد کنید:
import Order from '../../components/Order/Order';
حالا برای اینکه برنامه را تست کنیم یک سفارش جعلی را به صورت دستی می نویسیم تا ببینیم آیا سفارشات به شکل صحیح نمایش داده می شوند یا خیر:
class Orders extends Component { render() { return ( <div> <Order /> <Order /> </div> ); } }
فعلا فقط Order را نمایش می دهیم. مشکل بعدی این است که باید path را طوری مدیریت کنیم که کامپوننت Orders به جای burgerbuilder و checkout نمایش داده شوند. یعنی این سه مورد هیچ گاه نباید با هم نمایش داده شوند بنابراین به App.js می رویم و Orders.js را وارد می کنیم:
import Orders from './containers/Orders/Orders';
سپس درون دستور <Switch> یک path جدید برای orders ایجاد می کنیم:
<Switch> <Route path="/checkout" component={Checkout} /> <Route path="/orders" component={Orders} /> <Route path="/" exact component={BurgerBuilder} /> </Switch>
دستور switch کاری می کند که در هر لحظه فقط یکی از این سه کامپوننت نمایش داده شوند.
من می خواهم برای رفتن به این path از nav بالای صفحه استفاده کنم و یک لینک ایجاد کنم که کاربر با کلیک روی آن به کامپوننت Orders هدایت شود. برای این کار ابتدا وارد فایل NavigationItems.js شوید (پوشه ی components و سپس navigation). همانطور که می بینید لینک های ما در این قسمت قرار دارند:
<ul className={classes.NavigationItems}> <NavigationItem link="/" active>Burger Builder</NavigationItem> <NavigationItem link="/">Checkout</NavigationItem> </ul>
من می خواهم لینک دوم را مخصوص سفارش ها قرار دهم بنابراین آن را به شکل زیر تغییر می دهم:
<NavigationItem link="/orders">Orders</NavigationItem>
در قدم بعد وارد فایل NavigationItem.js بشوید چرا که باید مشکلی را برطرف کنیم. در حال حاضر درون این فایل از یک تگ ساده ی <a> استفاده می کنیم اما قبلا توضیح داده بودم که در هنگام استفاده از Routing بهتر است از تگ های <a> استفاده نکنیم. بهتر است به جای آن از <NavLink> استفاده کنیم. در ابتدا آن را در فایل NavigationItem.js وارد می کنیم:
import { NavLink } from 'react-router-dom';
دلیل استفاده ی من از <NavLink> به جای <Link> این است که <NavLink> به صورت خودکار کلاسی به نام active را به لینک اضافه می کند (در فصل قبل در مورد آن صحبت کرده بودیم) و من می خواهم از این کلاس آماده برای استایل دهی لینک ها استفاده کنم (اگر یادتان باشد کلاس active را در فایل NavigationItem.module.css تعریف کرده بودیم).
در مرحله ی بعد تگ <a> را به <NavLink> و href را به to تغییر می دهیم. همچنین <NavLink> به صورت خودکار کار اعمال استایل برای کلاس active را انجام می دهد بنابراین به شرطی که قبلا نوشته بودیم نیز نیازی نداریم. بنابراین با اعمال تمام این تغییرات کل محتویات فایل NavigationItem.js به شکل زیر است:
import React from 'react'; import { NavLink } from 'react-router-dom'; import classes from './NavigationItem.css'; const navigationItem = ( props ) => ( <li className={classes.NavigationItem}> <NavLink to={props.link}>{props.children}</NavLink> </li> ); export default navigationItem;
به فایل NavigationItems.js برگردید و active را از آن حذف کنید (دیگر نیازی به آن نداریم):
<NavigationItem link="/">Burger Builder</NavigationItem>
فایل ها را ذخیره کرده و به مرورگر بروید تا برنامه را تست کنیم. با باز شدن مرورگر متوجه می شویم که کلاس active برای هیچ لینکی اعمال نشده است!
حتی اگر از dev tools مرورگر لینک Burger Builder را بررسی کنیم متوجه می شویم که کلاس active را دارد اما استایل های آن روی این لینک اعمال نشده است. به نظر شما مشکل از کجاست؟
همانطور که می دانید ما از CSS Module ها استفاده می کنیم و اگر از جلسات مربوط به فصل استایل دهی یادتان باشد CSS Module ها کلاس های ما را می گیرند و نام آن ها را به یک شناسه ی یکتا تغییر می دهند. بنابراین وقتی در فایل NavigationItem.module.css یک کلاس active را تعریف می کنیم، به صورت active به مرورگر پاس داده نمی شود بلکه یک هش (hash) خاص به آن متصل می شود که کلاس را تغییر خواهد داد.
برای حل این مشکل به فایل NavigationItem.js بروید و خصوصیت activeClassName را به <NavLink> اضافه کنید:
<li className={classes.NavigationItem}> <NavLink to={props.link} activeClassName={classes.active}>{props.children}</NavLink> </li>
قبلا به شما گفته بودم که آدرس درون to به صورت پیشوند عمل می کند بنابراین وقتی فقط آدرس /
را داشته باشیم، تمام آدرس های دیگر نیز Active خواهند بود. چرا؟ به دلیل اینکه /
به عنوان پیشوند عمل کرده و تمام آدرس ها با /
شروع می شوند بنابراین تمام لینک ها active خواهند بود. همچنین برایتان توضیح دادم که راه حل این مشکل استفاده از exact است اما اگر آن را به شکل زیر اضافه کنیم:
<li className={classes.NavigationItem}> <NavLink to={props.link} exact activeClassName={classes.active}>{props.children}</NavLink> </li>
Exact برای تمام لینک ها در نظر گرفته می شود. ما می خواهیم آن را برای آدرس /
قرار دهیم بنابراین کد بالا مشکل ساز است. راه حل بهتر این است که ابتدا به فایل NavigationItems.js بروید و exact را به آدرس /
اضافه کنید:
<NavigationItem link="/" exact>Burger Builder</NavigationItem>
سپس به NavigationItem.js بروید و exact را فقط به آدرس هایی اضافه کنید که exact را دارند:
const navigationItem = (props) => ( <li className={classes.NavigationItem}> <NavLink to={props.link} exact={props.exact} activeClassName={classes.active}>{props.children}</NavLink> </li> );
حالا اگر به مروگر برویم، مشکل ما حل شده است. در قسمت بعد باید سفارشات را از سمت سرور دریافت کنیم.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.