Redux پیشرفته در پروژه همبرگرساز – ثبت سفارش

Advanced Redux in Burger Builder Project - Ordering

22 بهمن 1399
redux پیشرفته در پروژه ی همبرگر ساز – ثبت سفارش

در حال حاضر برنامه ما می تواند سفارشات کاربران را در پایگاه داده ثبت کند اما قرار است که در این فصل از redux استفاده کنیم بنابراین همین کار را خواهیم کرد. اولین کاری که می خواهم انجام دهم، مدیریت ثبت سفارش در redux است:

    orderHandler = ( event ) => {
        event.preventDefault();
        this.setState( { loading: true } );
        const formData = {};
        for (let formElementIdentifier in this.state.orderForm) {
            formData[formElementIdentifier] = this.state.orderForm[formElementIdentifier].value;
        }
        const order = {
            ingredients: this.props.ings,
            price: this.props.price,
            orderData: formData
        }
        axios.post( '/orders.json', order )
            .then( response => {
                this.setState( { loading: false } );
                this.props.history.push( '/' );
            } )
            .catch( error => {
                this.setState( { loading: false } );
            } );
    }

همانطور که در کد بالا مشاهده می کنید، در حال حاضر این کار با axios و درون متد orderHandler انجام می شود.

اولین قدم برای انجام این کار، مراجعه به فایل actionTypes.js و تعریف چند actionTypes جدید است. بر اساس کد بالا می توانیم یک action برای شروع عملیات ثبت خرید داشته باشیم (مثلا برای نمایش spinner) و سپس دو action دیگر برای ثبت با موفقیت و مشکل در ثبت سفارش تعریف کنیم. بنابراین آن ها را اضافه می کنیم:

export const PURCHASE_BURGER_SUCCESS = 'PURCHASE_BURGER_SUCCESS';
export const PURCHASE_BURGER_FAIL = 'PURCHASE_BURGER_FAIL';

فعلا با همین موارد شروع می کنیم. حالا در پوشه actions فایل orders.js را باز کرده و اول از همه actionTypes را وارد کنید:

import * as actionTypes from './actionTypes';

همچنین برای ثبت سفارشات به axios نیاز داریم بنابراین آن را نیز وارد فایل می کنیم:

import axios from '../../axios-orders';

حالا می توانیم اولین actionCreator خود را بنویسیم:

export const purchaseBurgerSuccess = ( id, orderData ) => {
    return {
        type: actionTypes.PURCHASE_BURGER_SUCCESS,
        orderId: id,
        orderData: orderData
    };
};

در این کد id همان id سفارشی است که ثبت خواهد شد و orderData نیز اطلاعات مربوط به سفارش است که مشاهده می فرمایید (محتویات همبرگر سفارش داده شده چه بوده است). حالا یک actionCreator دیگر برای غیر موفقیت آمیز بودن عملیات می نویسیم:

export const purchaseBurgerSuccess = ( id, orderData ) => {
    return {
        type: actionTypes.PURCHASE_BURGER_SUCCESS,
        orderId: id,
        orderData: orderData
    };
};

export const purchaseBurgerFail = ( error ) => {
    return {
        type: actionTypes.PURCHASE_BURGER_FAIL,
        error: error
    };
}

در اینجا فقط خطا را به همراه action ارسال می کنیم. هر دوی این actionCreator ها متقارن هستند اما سومین actionCreator ما نامتقارن خواهد بود:

export const purchaseBurgerStart = (orderData) => {
    
};

این Action قرار است زمانی ارسال شود که ما روی دکمه ORDER کلیک کنیم. من orderData را در همین تابع دریافت می کنم. حالا باید مثل همیشه برای کدهای نامتقارن یک تابع دیگر را برگردانیم که dispatch را به عنوان آرگومان ورودی می گیرد:

export const purchaseBurgerStart = (orderData) => {
    return dispatch => {
        axios.post()
    };
};

یعنی می خواهیم سفارش جدیدی را post کنیم. کد مربوط به این کار را قبلا در فایل ContactData.js نوشته بودیم. این قسمت:

    orderHandler = ( event ) => {
        event.preventDefault();
        this.setState( { loading: true } );
        const formData = {};
        for (let formElementIdentifier in this.state.orderForm) {
            formData[formElementIdentifier] = this.state.orderForm[formElementIdentifier].value;
        }
        const order = {
            ingredients: this.props.ings,
            price: this.props.price,
            orderData: formData
        }
        axios.post( '/orders.json', order )
            .then( response => {
                this.setState( { loading: false } );
                this.props.history.push( '/' );
            } )
            .catch( error => {
                this.setState( { loading: false } );
            } );
    }

قسمت مربوط به axios را از کد بالا کات کرده و به order.js انتقال دهید:

export const purchaseBurgerStart = (orderData) => {
    return dispatch => {
        axios.post('/orders.json', order)
            .then(response => {
                this.setState({ loading: false });
                this.props.history.push('/');
            })
            .catch(error => {
                this.setState({ loading: false });
            });
    };
};

حالا باید کد بالا را ویرایش کنیم تا در برنامه ما کار کند. ابتدا به جای پاس دادن order به axios.post باید orderData را پاس بدهیم. همچنین نیازی به setState کردن حالت loading نیست و دستور props.history.push نیز باید حذف شود. بنابراین:

export const purchaseBurgerStart = (orderData) => {
    return dispatch => {
        axios.post('/orders.json', orderData)
            .then(response => {
                
            })
            .catch(error => {
                
            });
    };
};

در مرحله بعد باید در هنگام موفقیت و دریافت response و همچنین در هنگام بروز خطا، اطلاعات و action مورد نیازشان را Dispatch کنیم:

export const purchaseBurgerStart = (orderData) => {
    return dispatch => {
        dispatch(purchaseBurgerStart());
        axios.post('/orders.json', orderData)
            .then(response => {
                console.log(response.data);
                dispatch(purchaseBurgerSuccess(response.data, orderData));
            })
            .catch(error => {
                dispatch(purchaseBurgerFail(error));
            });
    };
};

علاوه بر dispatch کردن این دو action، مقدار response.data را نیز console.log کرده ام تا بتوانیم پاسخ firebase را به طور کامل مشاهده کنیم و مطمئن شویم مقدار response.data صحیح می باشد.

هنوز کارمان تمام نشده است. ابتدا به ContactData.js بروید و به متد orderHandler نگاهی بیندازید:

    orderHandler = ( event ) => {
        event.preventDefault();
        this.setState( { loading: true } );
        const formData = {};
        for (let formElementIdentifier in this.state.orderForm) {
            formData[formElementIdentifier] = this.state.orderForm[formElementIdentifier].value;
        }
        const order = {
            ingredients: this.props.ings,
            price: this.props.price,
            orderData: formData
        }
    }

توجه داشته باشید که قسمت axios.post را از آن جدا کرده ایم. در حال حاضر نمی خواهم حالت loading را در این متد مدیریت کنم بنابراین باید آن را حذف کنیم:

    orderHandler = ( event ) => {
        event.preventDefault();
        const formData = {};
// بقیه کدها //

در مرحله بعد می خواهم خطاهای احتمالی این قسمت را نیز مدیریت کنم بنابراین باید HOC مربوط به مدیریت خطای خودمان (withErrorHandler) را وارد این فایل کنیم:

import withErrorHandler from '../../../hoc/withErrorHandler/withErrorHandler'

سپس به انتهای فایل رفته و دستور export آن را درون withErrorHandler قرار می دهیم:

export default connect(mapStateToProps, mapDispatchToProps)(withErrorHandler(ContactData, axios));

این کار فقط برای مدیریت خطاهای این قسمت است و ربطی به redux ندارد. در مرحله بعد من می خواهم action های جدیدی را که در همین جلسه تعریف کردیم به این کامپوننت متصل کنیم. برای انجام این کار مثل همیشه از mapDispatchToProps استفاده می کنیم:

const mapDispatchToProps = dispatch => {
    return {
        onOrderBurger: (orderData) => dispatch()
    };
};

حالا باید actionCreator خود را در آن قرار دهیم اما به آن فایل دسترسی نداریم بنابراین ابتدا به index.js درون پوشه actions می رویم و آن را نیز اضافه می کنیم:

export {
    addIngredient,
    removeIngredient,
    initIngredients
} from './burgerBuilder';
export {
    purchaseBurger
} from './order';

حالا به ContactData.js برمی گردیم و index.js را وارد آن می کنیم:

import * as actions from '../../../store/actions/index';

سپس می توانیم mapDispatchToProps را کامل کنیم:

const mapDispatchToProps = dispatch => {
    return {
        onOrderBurger: (orderData) => dispatch(actions.purchaseBurgerStart(orderData))
    };
};

در نهایت درون متد orderHandler در همین فایل، onOrderBurger را صدا می زنیم:

    orderHandler = ( event ) => {
        event.preventDefault();
  
        const formData = {};
        for (let formElementIdentifier in this.state.orderForm) {
            formData[formElementIdentifier] = this.state.orderForm[formElementIdentifier].value;
        }
        const order = {
            ingredients: this.props.ings,
            price: this.props.price,
            orderData: formData
        }

        this.props.onOrderBurger(order);
        
    }

در حال حاضر کدهای ما هنوز مشکل دارند. ما هیچ کدی برای مدیریت وضعیت loading ننوشته ایم بنابراین کدها با مشکل مواجه می شوند. در قسمت بعد این موضوع را تصحیح خواهیم کرد.

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

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