در قسمت قبلی توانستیم که محتویات همبرگر را از طریق state مدیریت کرده و به صورت پویا نمایش دهیم. حالا سوالی پیش می آید که اگر مقدار محتویات همبرگر همگی صفر باشند چه می شود؟
state = { ingredients: { salad: 0, bacon: 0, cheese: 0, meat: 0 } }
در این صورت در مرورگر شاهد چنین چیزی خواهیم بود:
کد ما هیچ مشکلی ندارد و خطایی هم دریافت نمی کنیم اما بهتر است به جای آنکه فقط یک همبرگر خالی به کاربر نمایش دهیم به او پیامی هم بدهیم که مثلا بگوید «لطفا محتویات همبرگر را انتخاب کنید». چرا که هیچکس همبرگر خالی سفارش نخواهد داد بنابراین نمایش هشدار حالت ایده آل خواهد بود. برای انجام این کار باید متغیر transformedIngredients را چک کنیم:
const transformedIngredients = Object.keys(props.ingredients).map(igKey => { return [...Array(props.ingredients[igKey])].map((_, i) => { return <BurgerIngredient key={igKey + i} type={igKey} />; }) });
اما این متغیر حتی اگر مقادیر state را صفر در نظر بگیریم، دارای چند آرایه خالی خواهد بود بنابراین چک کردن آن به سادگی دیگر آرایه ها نیست. فعلا مقدار transformedIngredients را console.log می کنیم تا شکل آن را ببینیم:
const transformedIngredients = Object.keys(props.ingredients).map(igKey => { return [...Array(props.ingredients[igKey])].map((_, i) => { return <BurgerIngredient key={igKey + i} type={igKey} />; }) }); console.log(transformedIngredients);
خروجی ما در مرورگر بدین شکل خواهد بود:
همانطور که می بینید transformedIngredients یک آرایه است که درون خود آرایه های خالی دارد بنابراین برای بررسی مقادیر درون آن نمی توانیم از متد length استفاده کنیم چرا که اگر از length استفاده کنیم مقدار خروجی ما 4 می شود یعنی خود آرایه های خالی عضو به حساب می آیند.
برای انجام این کار بهتر است آرایه را reduce کنیم. ()reduce یک تابع جاوا اسکریپتی است که یک تابع به عنوان پارامتر می گیرد؛ خود این تابع پارامتر دو پارامتر دیگر می گیرد که اولی مقدار قبلی و دومی مقدار فعلی است. این پارامترها به صورت خودکار توسط جاوا اسکریپت پاس داده می شوند.
بر اساس توضیح documentation شبکه توسعه دهندگان موزیلا تابع ()reduce تابعی جاوا اسکریپتی است که یک تابع کاهش دهنده (reducer) را روی تک تک اعضای یک آرایه اجرا می کند. شما خودتان مسئول نوشتن این تابع می باشید. به زبان ساده تر این تابع یک آرایه را به یک مقدار ساده (Value) تبدیل می کند. به این مثال ساده دقت کنید:
const array1 = [1, 2, 3, 4]; const reducer = (accumulator, currentValue) => accumulator + currentValue; console.log(array1.reduce(reducer)); console.log(array1.reduce(reducer, 5));
در اینجا یک آرایه به نام array1 داریم که درون یک ثابت قرار داده شده است. سپس یک متغیر دیگر به نام reducer تعریف کرده ایم که همان تابع کاهش دهنده ما است. در کد زیر:
console.log(array1.reduce(reducer));
مقادیر 1 + 2 + 3 + 4 با هم جمع می شوند و خروجی عدد 10 خواهد بود. در کد خط بعد:
console.log(array1.reduce(reducer, 5));
مقدار اولیه نیز پاس داده شده است (عدد 5) بنابراین مقادیر 5 + 1 + 2 + 3 + 4 با هم جمع می شوند و خروجی عدد 15 خواهد بود.
در یک مثال دیگر می خواهیم اعداد درون آرایه را گرد کرده و سپس همه را با هم جمع کنیم:
var numbers = [15.5, 2.3, 1.1, 4.7]; function getSum(total, num) { return total + Math.round(num); } numbers.reduce(getSum,0);
خروجی عدد 24 می باشد. توجه کنید که اگر مقدار initial value را به تابع reduce نمی دادیم مقدار 23.5 میشد. برای اطلاعات بیشتر Documentation این تابع را با دقت کامل بخوانید.
من این تابع را به ادامه تابع دوم map در آرایه transformedIngredients می چسبانم:
const transformedIngredients = Object.keys(props.ingredients).map(igKey => { return [...Array(props.ingredients[igKey])].map((_, i) => { return <BurgerIngredient key={igKey + i} type={igKey} />; }) }).reduce((arr, el) => { }, []);
نام پارامترهای مقدار قبلی (قبل از تغییر) و مقدار بعدی (بعد از تغییر) را arr (آرایه) و el (عنصر) گذاشته ام اما شما می توانید هر نامی برای آن ها انتخاب کنید. بدنه این تابع روی تمام اعضایی که از تابع دوم map پاس داده می شوند اجرا خواهد شد (چرا که به map چسبیده است) اما به غیر از بدنه یک مقدار دیگر نیز قبول می کند و آن initial value (به معنی مقدار اولیه) است. ما آن را یک آرایه خالی گذاشته ایم؛ تابع reduce روی تک تک عناصر برگشتی از map دوم اجرا می شود و تک تک آن ها را به initial value (مقدار اولیه) اضافه می کند. حالا می گوییم:
const burger = (props) => { const transformedIngredients = Object.keys(props.ingredients).map(igKey => { return [...Array(props.ingredients[igKey])].map((_, i) => { return <BurgerIngredient key={igKey + i} type={igKey} />; }) }).reduce((arr, el) => { return arr.concat(el); }, []);
در واقع el (عنصر) را در arr (آرایه) ادغام کردیم. حالا اگر به مرورگر برویم چنین چیزی را مشاهده خواهیم کرد:
یعنی آرایه های خالی حذف شده اند. حالا اگر برای مثال دو قطعه پنیر به state اضافه کنیم:
state = { ingredients: { salad: 0, bacon: 0, cheese: 2, meat: 0 } }
در مرورگر (قسمت console) یک آرایه می گیریم که دارای 2 شیء است (یک شیء برای هر تکه پنیر). اگر هنوز متوجه نحوه کار این کد نشدید بهتر است documentation تابع reduce و map را به دقت مطالعه کنید.
حالا می توانیم به راحتی چک کنیم که آیا آرایه ما خالی است یا خیر. برای شروع بهتر است transformedIngredients را تبدیل به let کنیم تا دیگر ثابت نباشد:
let transformedIngredients = Object.keys(props.ingredients).map(igKey => { return [...Array(props.ingredients[igKey])].map((_, i) => { return <BurgerIngredient key={igKey + i} type={igKey} />; }) }).reduce((arr, el) => { return arr.concat(el); }, []);
حالا می گوییم:
if (transformedIngredients.length === 0) { transformedIngredients = <p>Please start adding ingredients!</p>; }
مطمئن شوید که مقادیر State همگی صفر باشند (از فایل BurgerBuilder.js):
state = { ingredients: { salad: 0, bacon: 0, cheese: 0, meat: 0 } }
سپس مرورگر را باز کنید:
(کدهای قرار گرفته برای دانلود با روش eject کردن پروژه برای CSS Module نوشته شده اند اما در خود پروژه از روش جدید استفاده کرده ایم. هنگام کار با آن ها به این نکته توجه داشته باشید). در قسمت بعد کنترل های کاربر را خواهیم ساخت.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.