در قسمت های قبلی کدهای زیادی نوشتیم و قسمت های مختلفی را مدیریت کردیم تا اینکه به ساخت دکمه های کنترل رسیدیم. در حال حاضر این دکمه ها به شکل زیر هستند:
اما هیچ کاری انجام نمی دهند. بنابراین باید آن ها را به State متصل کنیم. برای شروع در فایل BurgerBuilder.js دو متد مختلف تعریف می کنیم:
addIngredientHandler = (type) => { } removeIngredientHandler = (type) => { }
متد اول برای اضافه کردن محتویات به همبرگر و متد دوم برای حذف محتویات می باشد که هر دو type یا نوع محتویات را می گیرند تا بدانند چه چیزی را حذف یا اضافه کنند. برای اضافه کردن یا کم کردن باید ابتدا تعداد مخلفات و محتویات فعلی همبرگر را بدانیم، بنابراین:
addIngredientHandler = (type) => { const oldCount = this.state.ingredients[type]; const updatedCount = oldCount +1; }
یعنی تعداد یک ماده غذایی را از state گرفته ایم و یک عدد به آن اضافه کرده ایم. اما این کد هنوز مشکلی دارد، اگر یادتان باشد گفتیم که State باید به شکل immutable تغییر کند و سریعا بروزرسانی شود. بنابراین:
addIngredientHandler = (type) => { const oldCount = this.state.ingredients[type]; const updatedCount = oldCount +1; const updatedIngredients = { ...this.state.ingredients }; updatedIngredients[type] = updatedCount; }
با استفاده از اپراتور spread مقدار ingredient را جدا کرده و درون یک شیء به نام updatedIngredients گذاشته ایم. سپس مقدار آن را برابر با updatedCount قرار داده ایم.
حالا بهتر است یک خصوصیت دیگر به state اضافه کنیم!
state = { ingredients: { salad: 0, bacon: 0, cheese: 0, meat: 0 }, totalPrice: 4 }
خصوصیت totalPrice به معنی قیمت همبرگر است و ما گفته ایم که حداقل قیمت همبرگر 4 دلار خواهد بود (شما میتوانید عدد دیگری انتخاب کنید) و رستوران ما همبرگر ارزان تری ندارد. برای محاسبه قیمت ها باید قیمت تک تک محتویات همبرگر را بدانیم، بنابراین بیرون از کلاس BurgerBuilder (بعد از دستور های import) یک ثابت برای این قیمت ها تعریف خواهیم کرد:
const INGREDIENT_PRICES = { salad: 0.5, cheese: 0.4, meat: 1.3, bacon: 0.7 };
حالا به متد addIngredientHandler برمی گردیم و قیمت را محاسبه می کنیم:
addIngredientHandler = (type) => { const oldCount = this.state.ingredients[type]; const updatedCount = oldCount +1; const updatedIngredients = { ...this.state.ingredients }; updatedIngredients[type] = updatedCount; const priceAddition = INGREDIENT_PRICES[type]; const oldPrice = this.state.totalPrice; const newPrice = oldPrice + priceAddition; }
کد واضح و ساده است بنابراین نیازی به توضیحات اضافی ندارد. حالا باید setState را صدا بزنیم تا state بروزرسانی شود:
addIngredientHandler = (type) => { const oldCount = this.state.ingredients[type]; const updatedCount = oldCount + 1; const updatedIngredients = { ...this.state.ingredients }; updatedIngredients[type] = updatedCount; const priceAddition = INGREDIENT_PRICES[type]; const oldPrice = this.state.totalPrice; const newPrice = oldPrice + priceAddition; this.setState({ totalPrice: newPrice, ingredients: updatedIngredients }); }
به همین سادگی!
حالا باید این متد را به دکمه more بچسبانیم. برای این کار باید یک خصوصیت را به BuildControls (در قسمت JSX) پاس بدهیم:
render() { return ( <Aux> <Burger ingredients={this.state.ingredients} /> <BuildControls ingredientAdded={this.addIngredientHandler} /> </Aux> ); }
حالا به فایل BuildControls.js میرویم تا این خصوصیت را به کامپوننت مورد نظرش پاس بدهیم. در قسمت JSX این فایل می گوییم:
const buildControls = (props) => ( <div className={classes.BuildControls}> {controls.map(ctrl => ( <BuildControl key={ctrl.label} label={ctrl.label} added={props.ingredientAdded} /> ))} </div> );
اما یک مشکل وجود دارد. ما در کد بالا متد ingredientAdded را به تک تک کنترل ها داده ایم اما از کجا بدانیم که کاربر روی دکمه more مربوط به پنیر کلیک کرده یا گوشت یا چیز دیگری؟ بنابراین باید type را هم پاس بدهیم و کد بالا را ویرایش کنیم. می توانیم آن را بدین شکل بنویسیم:
const buildControls = (props) => ( <div className={classes.BuildControls}> {controls.map(ctrl => ( <BuildControl key={ctrl.label} label={ctrl.label} type={ctrl.type} added={props.ingredientAdded} /> ))} </div> );
اما این روش باعث یک گردش اضافی در تابع map می شود. به جای نوشتن کدها به شکل بالا می توانیم آن را به شکل زیر بنویسیم:
const buildControls = (props) => ( <div className={classes.BuildControls}> {controls.map(ctrl => ( <BuildControl key={ctrl.label} label={ctrl.label} added={() => props.ingredientAdded(ctrl.type)} /> ))} </div> );
با این کار مقدار added را تبدیل به یک تابع کرده ایم که متد ingredientAdded را اجرا می کند اما type را نیز به عنوان آرگومان می گیرد. اگر یادتان باشد تابع addIngredientHandler مقدار type را به عنوان آرگومان دریافت می کرد:
addIngredientHandler = (type) => { const oldCount = this.state.ingredients[type]; const updatedCount = oldCount + 1; const updatedIngredients = { ...this.state.ingredients }; updatedIngredients[type] = updatedCount; const priceAddition = INGREDIENT_PRICES[type]; const oldPrice = this.state.totalPrice; const newPrice = oldPrice + priceAddition; this.setState({ totalPrice: newPrice, ingredients: updatedIngredients }); }
بنابراین همین جا type را به این تابع پاس می دهیم.
اما کار ما هنوز تمام نشده است! ما باید خصوصیت added را (که حاوی تابع است) به دکمه more متصل کنیم. بنابراین به فایل BuildControl.js رفته و آن را به شکل زیر ویرایش می کنیم:
const buildControl = (props) => ( <div className={classes.BuildControl}> <div className={classes.Label}>{props.label}</div> <button className={classes.Less}>Less</button> <button className={classes.More} onClick={props.added}>More</button> </div> );
به همین سادگی توانستیم added را به دکمه more متصل کنیم. حالا تمامی فایل ها را ذخیره کرده و به مرورگر بروید. حواستان باشد که دستور npm start همیشگی خودمان را زده باشید تا کدهای مرورگر به روز شود. در ابتدای کار با چنین چیزی مواجه خواهید شد:
اما اگر دکمه های more را بزنید باید شاهد افزایش مقدار ماده غذایی مربوطه باشید:
حالا که کدهای قسمت more تکمیل شده است باید کدهای دکمه less را هم بنویسیم چرا که فعلا نمی توانیم مقدار محتویات اضافه شده را کم کنیم. این کار را در قسمت بعد انجام خواهیم داد.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.