نمایش loading هنگام ارسال درخواست

نمایش loading هنگام ارسال درخواست

نمایش loading هنگام ارسال درخواست

در جلسه ی قبل ارسال درخواست های post را کدنویسی کرده و یک ورودی را نیز در پایگاه داده ی خودمان ایجاد کردیم اما در این جلسه می خواهیم کاری کنیم که هنگام ارسال درخواست و تا رسیدن پاسخ، یک صفحه ی loading (با استفاده از spinner ها) برای کاربر نمایش داده شود تا کاربر بداند برنامه ی ما هنگ نکرده است بلکه منتظر پاسخ از سمت سرور می باشد. البته باید اشاره کنم که Firebase بسیار بسیار سریع است بنابراین به احتمال زیاد مهلتی به ما نمی دهد که بخواهیم صفحه ی loading خود را ببینیم اما باز هم بهتر است این کار را انجام دهید چرا که اگر شبکه ی کاربر بسیار کُند باشد (که اکثرا در ایران همینطور است) دیگر مسئله به Firebase مربوط نیست و چند ثانیه ای طول می کشد تا خود کاربر پاسخ را دریافت کند و صفحه ی loading کارآمد خواهد بود.

در واقع من میخواهم یک Spinner (علامت های loading) را داخل عنصر <Modal> در BurgerBuilder.js تعریف کنم و به جای <OrderSummary> نمایش دهم. منظورم این قسمت است:

<Modal show={this.state.purchasing} modalClosed={this.purchaseCancelHandler}>
    <OrderSummary
        ingredients={this.state.ingredients}
        price={this.state.totalPrice}
        purchaseCancelled={this.purchaseCancelHandler}
        purchaseContinued={this.purchaseContinueHandler}
    />
</Modal>

برای انجام این کار به یک کامپوننت spinner نیاز داریم که یک عنصر UI است بنابراین وارد پوشه ی UI شده و یک پوشه ی دیگر به نام Spinner در آن ایجاد کنید که حاوی یک فایل به نام Spinner.js باشد. مثل همیشه محتویات این فایل برای شروع باید به شکل زیر باشد:

import React from 'react';

const spinner = () => (
    
);

export default spinner;

برای ایجاد یک spinner به راحتی میتوانیم عبارت css spinners را در گوگل جست و جو کنیم. نتیجه ی اول یکی از بهترین نتایجی است که گوگل به ما نمایش می دهد (https://projects.lukehaas.me/css-loaders/). با ورود به این سایت به چنین صفحه ای می رسید:

نمونه هایی از css spinner ها
نمونه هایی از css spinner ها

همانطور که در تصویر بالا می بینید، در سمت چپ و بالای این spinner ها دو دکمه وجود دارد: BG و FG که مخفف Background و Foreground هستند. روی آن ها کلیک کنید و مقدارشان را به ترتیب روی FFFFFF (سفید) و 521751 (بنفش تیره) قرار دهید (در قسمت Hex Color آن ها). اگر از ویندوز استفاده میکنید احتمالا قسمت Hex را نداشته باشید و ویندوز از شما بخواهد مقادیر را به صورت RGB وارد کنید که در این صورت نیز میتوان گفت BG را روی (255,255,255) و FG را روی (82,23,81) قرار دهید.

حالا روی یکی از Spinner هایی که دوست دارید کلیک کنید تا کدهای CSS آن را مشاهده کنید. تمام کدها را کپی و در فایل جدیدی به نام Spinner.module.css قرار دهید:

.Loader, .Loader:before, .Loader:after {
    border-radius: 50%;
}

.Loader {
    color: #521751;
    font-size: 11px;
    text-indent: -99999em;
    margin: 55px auto;
    position: relative;
    width: 10em;
    height: 10em;
    box-shadow: inset 0 0 0 1em;
    -webkit-transform: translateZ(0);
    -ms-transform: translateZ(0);
    transform: translateZ(0);
}

.Loader:before, .Loader:after {
    position: absolute;
    content: '';
}

.Loader:before {
    width: 5.2em;
    height: 10.2em;
    background: #521751;
    border-radius: 10.2em 0 0 10.2em;
    top: -0.1em;
    left: -0.1em;
    -webkit-transform-origin: 5.2em 5.1em;
    transform-origin: 5.2em 5.1em;
    -webkit-animation: load2 2s infinite ease 1.5s;
    animation: load2 2s infinite ease 1.5s;
}

.Loader:after {
    width: 5.2em;
    height: 10.2em;
    background: #521751;
    border-radius: 0 10.2em 10.2em 0;
    top: -0.1em;
    left: 5.1em;
    -webkit-transform-origin: 0px 5.1em;
    transform-origin: 0px 5.1em;
    -webkit-animation: load2 2s infinite ease;
    animation: load2 2s infinite ease;
}

@-webkit-keyframes load2 {
    0% {
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
    }
    100% {
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

@keyframes load2 {
    0% {
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
    }
    100% {
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

و کد HTML مربوط به spinner را نیز درون کامپوننت Spinner.js قرار میدهم. این کد در همان سایت موجود است. همچنین کلاس ها را نیز وارد این کامپوننت می کنیم بنابراین:

import React from 'react';
import classes from './Spinner.module.css';

const spinner = () => (
    <div className={classes.Loader}>Loading...</div>
);

export default spinner;

حالا وارد BurgerBuilder.js میشویم. ما برای نمایش مشروط Modal و OrderSummary نیاز به یک خصوصیت جدید درون state داریم. بنابراین می گوییم:

state = {
    ingredients: {
        salad: 0,
        bacon: 0,
        cheese: 0,
        meat: 0
    },
    totalPrice: 4,
    purchasable: false,
    purchasing: false,
    loading: false
}

خصوصیت loading را اضافه کرده و در حالت پیش فرض false قرار داده ایم. حالا spinner را وارد این فایل می کنیم:

import Spinner from '../../components/UI/Spinner/Spinner';

 یک متغیر به نام orderSummary ایجاد می کنیم و عنصر <OrderSummary> را از قسمت JSX بیرون آورده و در آن قرار می دهیم:

let orderSummary = <OrderSummary
ingredients={this.state.ingredients}
price={this.state.totalPrice}
purchaseCancelled={this.purchaseCancelHandler}
purchaseContinued={this.purchaseContinueHandler}
/>;

 سپس با یک شرط if چک می کنیم که loading در state فعال باشد که اگر فعال بود مقدار آن متغیر را از orderSummary به spinner خودمان تغییر می دهیم.

if (this.state.loading) {
    orderSummary = <Spinner />;
}

در نهایت نیز این متغیر را به صورت پویا در <Modal> نمایش می دهیم بنابراین کد کامل این قسمت درون متد render و به شکل زیر است:

let orderSummary = <OrderSummary
    ingredients={this.state.ingredients}
    price={this.state.totalPrice}
    purchaseCancelled={this.purchaseCancelHandler}
    purchaseContinued={this.purchaseContinueHandler}
/>;

if (this.state.loading) {
    orderSummary = <Spinner />;
}

return (
    <Aux>
        <Modal show={this.state.purchasing} modalClosed={this.purchaseCancelHandler}>
            {orderSummary}
        </Modal>
        <Burger ingredients={this.state.ingredients} />
        <BuildControls
            ingredientAdded={this.addIngredientHandler}
            ingredientRemoved={this.removeIngredientHandler}
            disabled={disabledInfo}
            purchasable={this.state.purchasable}
            price={this.state.totalPrice}
            ordered={this.purchaseHandler}
        />
    </Aux>
);

حالا داخل purchaseContinueHandler باید state را تغییر دهیم تا spinner به صورت دائم نمایش داده نشود:

purchaseContinueHandler = () => {
    // alert('You continue!')
    this.setState({ loading: true })
    const order = {
        ingredients: this.state.ingredients,
        price: this.state.totalPrice,
        customer: {
            name: 'Amir Zouerami',
            address: {
                street: 'Teststreet 1',
                zipCode: '9174582541',
                country: 'Iran'
            },
            email: 'test@test.com'
        },
        deliveryMethod: 'fastest'
    }
    axios.post('/orders.json', order)
        .then(response => {
            this.setState({ loading: false });
        })
        .catch(error => {
            this.setState({ loading: false });
        });
}

تغییراتی که در این متد داده ایم عبارت اند از:

  • در ابتدای کار مقدار loading را روی true گذاشته ایم تا به محض کلیک، spinner ما نمایش داده شود.
  • هنگام دریافت پاسخ مقدار loading را روی false گذاشته ایم تا دیگر spinner نمایش داده نشود. حتی اگر درخواست موفقیت آمیز نباشد، باید spinner متوقف شود چرا که در حال بارگذاری یا پردازش چیزی نیستیم.
  • در نهایت در صورت دریافت خطا نیز باید loading را false کنیم تا spinner متوقف شود.

البته هنوز کار ما تمام نشده است. ما می خواهیم در نهایت Modal را هم ببندیم بنابراین قسمت آخر این کد را بدین صورت ویرایش می کنیم:

axios.post('/orders.json', order)
        .then(response => {
            this.setState({ loading: false, purchasing: false });
        })
        .catch(error => {
            this.setState({ loading: false, purchasing: false });
        });

چرا چنین کاری را کردیم؟ به دلیل اینکه Modal تنها زمانی نمایش داده می شود که purchasing روی true باشد و با false کردن آن Modal نیز بسته خواهد شد. حالا به مرورگر می رویم تا این مسئله را تست کنیم. متاسفانه اگر یک همبرگر بسازید و آن را سفارش بدهید، spinner ما نمایش داده نمی شود! چرا؟

یک راهنمایی میکنم: Modal به درستی بروزرسانی نمی شود!

برای بررسی به فایل Modal.js می رویم و به کد زیر برمیخوریم:

    shouldComponentUpdate(nextProps, nextState) {
        return nextProps.show !== this.props.show;
    }

در واقع ما تنها خصوصیت Show درون state را چک می کنیم و تنها در صورتی که show تغییر کند، Modal را بروزرسانی می کنیم اما ما props.children را تغییر می دهیم (یک orderSummary جدید را پاس می دهیم) بنابراین این کد را به شکل زیر ویرایش می کنیم:

    shouldComponentUpdate(nextProps, nextState) {
        return nextProps.show !== this.props.show || nextProps.children !== this.props.children;
    }

بدین صورت تغییر props.children را نیز بررسی می کنیم.

حالا اگر به مرورگر بروید و یک سفارش را ثبت کنید، برای چند ثانیه متوجه spinner می شوید اما ظاهر آن کمی خراب است. بنابراین کدهای spinner.module.css را به شکلی که می گویم تصحیح کنید؛ برای قسمت Loader:before و خصوصیت background، مقدار fff# را قرار می دهیم:

.Loader:before {
    width: 5.2em;
    height: 10.2em;
    background: #fff;
    border-radius: 10.2em 0 0 10.2em;
    top: -0.1em;
    left: -0.1em;
    -webkit-transform-origin: 5.2em 5.1em;
    transform-origin: 5.2em 5.1em;
    -webkit-animation: load2 2s infinite ease 1.5s;
    animation: load2 2s infinite ease 1.5s;
}

و برای قسمت Loader:after نیز دقیقا همین کار را انجام می دهیم. حالا اگر دوباره تست کنید، spinner را به صورت صحیح مشاهده خواهید کرد.

نکته: در صورتی که هنگام ثبت سفارش در قسمت console مرورگر با خطا مواجه می شوید، باید ابزار دور زدن تحریم خود را روشن کنید چرا که Firebase ایران را تحریم کرده است بنابراین هنگام ثبت سفارش و ارسال درخواست به Firebase پاسخ ما عدم دسترسی به پایگاه داده و سرور خواهد بود. این عدم دسترسی به صورت یک خطا در قسمت console مرورگر نمایش داده می شود.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری دوره جامع آموزش ری اکت توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.