درک عملیات‌های پشت صحنه در mapActions

Behind-the-scenes Operations in mapActions

Vue.JS 2: درک عملیات های پشت صحنه در mapActions - قسمت 95

در قسمت قبل در مورد مشکلی صحبت کردیم که با mutation ها داشتیم و آن مشکل synchronous بودن (همگام بودن یا نامتقارن بودن) mutation ها بود. در همان قسمت توضیح دادم که نیاز ما به انجام عملیات های ناهمگام (async) غیر قابل انکار است. ما دائما در حال تبادل داده با سرور هستیم و نمی توانیم آن را نادیده بگیریم. بنابراین چطور می توانیم مشکل sync (همگام) بودن mutation ها را حل کنیم؟ VueX برای حل این مشکل از قابلیتی به نام Action استفاده می کند. Action ها یک واحد میانی بین کامپوننت و mutation هستند که عملیات های async را اجرا کرده و سپس تغییر را commit می کنند. به عبارت دیگر کامپوننت ما به action می گوید که فلان عملیات async را انجام بده. action آنقدر منتظر می ماند که عملیات async تمام شود و پس از آن commit را انجام می دهد.

من در حال حاضر چهار actions را در فایل store.js خودم دارم:

actions: {
    increment: ({ commit }) => {
        commit('increment');
    },
    decrement: ({ commit }) => {
        commit('decrement');
    },
    asyncIncrement: ({ commit }) => {
        setTimeout(() => {
            commit('increment');
        }, 1000);
    },
    asyncDecrement: ({ commit }) => {
        setTimeout(() => {
            commit('decrement');
        }, 1000);
    }
}

همانطور که قبلا هم گفته ام، ما در این دوره از حالت پیشنهادی (استفاده از action ها حتی در عملیات های sync) استفاده خواهیم کرد بنابراین دیگر نیازی به اجرای مستقیم mutation ها نداریم. بنابراین به فایل AnotherCounter رفته و mapMutations را حذف می کنیم:

<script>
import {  } from "vuex";
export default {
  methods: {

}
};
</script>

منتهی برای آنکه از action ها به صورت دستی استفاده نکنیم، از متد کمکی mapActions استفاده خواهیم کرد تا کارمان سریع تر شود:

<script>
import { mapActions } from "vuex";
export default {
  methods: {
    ...mapActions(["increment", "decrement"])
  }
};
</script>

همانطور که می بینید استفاده از action ها دقیقا مشابه با استفاده از mutation ها است اما نکات مهمی وجود دارد که باید آن ها را درک کنیم. در ابتدا باید درک کنیم که mapActions در نهایت چه چیزی را برای ما می سازد یا در نهایت به چه چیزی تبدیل می شود. زمانی که mapActions اجرا می شود، دو action به نام های increment و decrement را صدا می زند. بنابراین پس از اجرای آن کد بالا معادل کد زیر است:

<script>
import { mapActions } from "vuex";
export default {
  methods: {
    increment() {
      this.$store.dispatch("increment");
    },
    decrement() {
      this.$store.dispatch("decrement");
    }
  }
};
</script>

یعنی mapActions در نهایت متدی به نام dispatch (به معنی «اعزام» یا «ارسال») را صدا می زند. dispatch یک آرگومان می گیرد که همان نامِ action است. روند کاری این dispatch شبیه به فصل event ها و event bus است. زمانی که dispatch صدا زده می شود، نام action به فایل Store.js ارسال می شود بنابراین action مورد نظر اجرا خواهد شد.

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

increment(by) {
    this.$store.dispatch("increment", by);
}

by می تواند مقدار اضافه شده به counter باشد (در انگلیسی می گوییم multiply by 4 که یعنی در 4 ضرب کن یا increase by 100 که یعنی 100 واحد اضافه کن). این مقدار کاملا سلیقه ای است و می تواند هر چیزی باشد. سپس برای صدا زدن increment می توان گفت:

<template>
  <div>
    <button class="btn btn-primary" @click="increment(100)">Increment</button>
    <button class="btn btn-primary" @click="decrement">Decrement</button>
  </div>
</template>

یعنی به increment مقدار 100 را پاس داده ایم تا 100 واحد را به شمارنده (counter) اضافه کنیم. خوشبختانه نیازی نیست که متدهای خودمان را بدین شکل و به صورت دستی ارسال کنیم و تنها دلیل نوشتن دستی متدها این بود که بدانید در پشت صحنه چه اتفاقی می افتد. بنابراین کدهای MapActions را به حالت قبلی خود در بیاورید:

<template>
  <div>
    <button class="btn btn-primary" @click="increment(100)">Increment</button>
    <button class="btn btn-primary" @click="decrement">Decrement</button>
  </div>
</template>

<script>
import { mapActions } from "vuex";
export default {
  methods: {
    ...mapActions(["increment", "decrement"])
  }
};
</script>

mapActions به صورت خودکار اگر ببیند که شما به increment یا decrement مقدار خاصی را پاس داده اید، خودش آن را به عنوان آرگومان دوم dispatch می کند و نیازی به نوشتن دستی نیست. حالا به فایل store.js می رویم و این آرگومان را در آن دریافت می کنیم:

actions: {
    increment: ({ commit }, payload) => {
        commit('increment', payload);
    },
    decrement: ({ commit }) => {
        commit('decrement');
    },
// بقیه کدها //

بنابراین در ابتدا یک مقدار دلخواه به سمت action ها dispatch (ارسال) می شود. حالا در اینجا (درون action مورد نظر) این مقدار دلخواه را به عنوان آرگومان دوم دریافت کرده ایم که معمولا نام آن را payload می گذارند اما شما می توانید نام دیگری انتخاب کنید. سپس این payload را که در action خود گرفته ایم به commit پاس می دهیم تا به mutation ها ارسال شود. در مرحله بعد باید به mutation مورد نظر برویم و بگوییم:

mutations: {
    increment: (state, payload) => {
        state.counter += payload;
    },
    decrement: state => {
        state.counter--;
    }
},
actions: {
    increment: ({ commit }, payload) => {
// بقیه کدها //

یعنی مقدار شمارنده (counter) را به اندازه payload اضافه کن. شما باید همین کار را برای فایل Counter.vue نیز انجام بدهید. حالا اگر کدها را ذخیره کنیم و به مرورگر برویم، همه چیز بدون مشکل کار می کند. البته توجه داشته باشید که با هر کلیک 200 واحد به counter اضافه می شود. چرا؟ حتما یادتان هست که در getter قبل از دریافت state، خصوصیات را در 2 ضرب کرده بودیم.

در نهایت این کار را برای متد decrement در mutation نیز انجام می دهیم:

mutations: {
    increment: (state, payload) => {
        state.counter += payload;
    },
    decrement: (state, payload) => {
        state.counter -= payload;
    }
},

سپس decrement درون action را نیز بدین صورت ویرایش می کنیم:

actions: {
    increment: ({ commit }, payload) => {
        commit('increment', payload);
    },
    decrement: ({ commit }, payload) => {
        commit('decrement', payload);
    },
// بقیه کدها //

سپس به فایل های counter.vue و anotherCounter.vue بروید و 50 واحد را هنگام صدا زدن decrement پاس بدهید:

<template>
  <div>
    <button class="btn btn-primary" @click="increment(100)">Increment</button>
    <button class="btn btn-primary" @click="decrement(50)">Decrement</button>
  </div>
</template>

حالا می توانید کدها را در مرورگر تست کنید.

سوال بعدی اینجاست که اگر بخواهیم بیشتر از یک آرگومان را به عنوان payload پاس بدهیم، چه کار باید بکنیم؟ بگذارید این مسئله را در قالب استفاده از action های async خود توضیح بدهم. من قصد دارم فایل AnotherCounter.vue را مخصوص کدهای async خودمان قرار بدهم که در آن دو مقدار را به عنوان payload پاس بدهیم (dispatch کنیم). اولین مقدار، میزان کم یا اضافه شدن counter و دومین مقدار مدت زمان برای setTimeout باشد. مشکل اینجاست که dispatch فقط یک آرگومان می گیرد بنابراین برای حل این مشکل باید یک شیء جاوا اسکریپتی ارسال کنیم. برای این کار به فایل AnotherCounter.vue رفته و محتویات آن را به شکل زیر تغییر می دهیم:

<template>
  <div>
    <button class="btn btn-primary" @click="asyncIncrement({by: 50, duration: 500})">Increment</button>
    <button class="btn btn-primary" @click="asyncDecrement({by: 50, duration: 500})">Decrement</button>
  </div>
</template>

<script>
import { mapActions } from "vuex";

export default {
  methods: {
    ...mapActions(["asyncIncrement", "asyncDecrement"])
  }
};
</script>

همانطور که می بینید به mapAction مقادیر asyncIncrement و asyncDecrement را داده ام تا از عملیات async خود استفاده کنیم. همچنین برای صدا زدن آن ها در template از یک شیء استفاده کرده ام که دو خصوصیت درون خود دارد: by و duration که نام هایی سلیقه ای هستند. در مرحله بعد به store.js می رویم تا این مقادیر dispatch شده را دریافت کنیم:

actions: {
    increment: ({ commit }, payload) => {
        commit('increment', payload);
    },
    decrement: ({ commit }, payload) => {
        commit('decrement', payload);
    },
    asyncIncrement: ({ commit }, payload) => {
        setTimeout(() => {
            commit('increment', payload.by);
        }, payload.duration);
    },
    asyncDecrement: ({ commit }, payload) => {
        setTimeout(() => {
            commit('decrement', payload.by);
        }, payload.duration);
    }
}

به همین راحتی می توانیم از این شیء استفاده کنیم. حالا به  مرورگر بروید و کدها را تست کنید. با کلیک روی دکمه های مربوط به AnotherCounter.vue، حدود نیم ثانیه تاخیر مشاهده می کنید. این هم از نحوه اجرای کدهای async!

windows 7 home basic lizenz kaufen

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

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

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