Home > other >  nuxt vuex: do not mutate vuex store state outside mutation handlers
nuxt vuex: do not mutate vuex store state outside mutation handlers

Time:12-06

Why do I get this error:

Error [vuex] Do not mutate vuex store state outside mutation handlers

This happens when I call this component.

 <template lang="pug">
  .auth-popup
    button.auth-popup__button-top.auth-popup__button-top_close(
      type='button'
      aria-label='Закрыть форму авторизации'
      @click='$emit(`close`)'
    )
    h2.auth-popup__title Вход или регистрация

    form.auth-popup__form(@submit.prevent='submitHandler')
      input.auth-popup__fake-input(v-show='showFakeInput' aria-hidden='true' autocomplete='off' ref='fakeInput')

      label.auth-popup__label(for='authLogin') Телефон
      input#authLogin.auth-popup__input(
        type='tel'
        autocomplete='tel'
        v-model='login'
        @input='inputHandler'
        ref='loginInput'
      )

      p.auth-popup__error(v-if='login && !isPhoneAuth') Телефон указан неверно
      p.auth-popup__error(v-if='error' v-html='error')
      p.auth-popup__timer(v-if='getCodeTimer' v-html='codeTimerMessage')

      button.auth-popup__button-send(
        type='submit'
        :disabled='!isLoginLength || !isPhoneAuth || getCodeTimer || showPreloader'
      )
        span.auth-popup__button-inner(v-if='!showPreloader') Получить код
        Preloader.auth-popup__preloader(:show='showPreloader' :color='`#ffffff`')

    button.auth-popup__button-email(
      type='button'
      @click='$emit(`email`)'
    ) Войти по почте
</template>

<script>
import { mapActions, mapMutations, mapGetters } from 'vuex'
import { REGEXPS } from '~/assets/js/utils/constants/regexps';
import { MESSAGES } from "~/assets/js/utils/constants/messages";
import delay from '~/assets/js/utils/functions/promiseTimeout';

import Preloader from "~/components/modules/Preloader"

export default {
  name: 'AuthPhone',
  components: {
    Preloader
  },
  data() {
    return {
      showFakeInput: false,
      showPreloader: false,
      login: '',
      error: ''
    }
  },
  computed: {
    isPhoneAuth() {
      return REGEXPS.FULL_PHONE_SYMBOLS.test(this.login);
    },
    isLoginLength() {
      const phoneDigits = this.login.trim().replace(/\D/g, ``);
      return phoneDigits.length > 9;
    },
    createPhoneValue() {
      let phoneNumber = this.login;
      if (phoneNumber.startsWith('8')) {
        phoneNumber = '7'   phoneNumber.slice(1);
      }
      return ` ${phoneNumber.replace(/\D /g, '')}`;
    },
    ...mapGetters({
      getAuthResponse: 'authorization/getAuthResponse',
      getCodeTimer: 'authorization/getCodeTimer',
      codeTimerMessage:'authorization/codeTimerMessage'
    })
  },
  methods: {
    ...mapActions({
      authRequest: 'authorization/authRequest'
    }),
    ...mapMutations({
      startCodeTimer: 'authorization/startCodeTimer',
      resetCodeTimer: 'authorization/resetCodeTimer'
    }),
    inputHandler() {
      this.error = '';
      if (this.getCodeTimer) {
        this.resetCodeTimer();
      }
    },
    async submitHandler() {
      this.showPreloader = true;
      const sendData = {
        ident_method: `PHONE`,
        login: this.createPhoneValue
      };
      await this.authRequest(sendData)
        .then(() => {
          this.showPreloader = false;
          const data = this.getAuthResponse;
          if (data.result) {
            if (data.is_registered && !data.is_active) {
              this.error = MESSAGES.ERROR.ACCOUNT_DEACTIVATED;
            } else if (data.is_code_sended) {
              this.startCodeTimer(30);
              this.$emit('enter');
            }
          } else if (MESSAGES.ERROR[data.error]) {
            this.error = MESSAGES.ERROR[data.error];
          } else {
            this.error = data.error;
          }
        });
    },

  },
  mounted() {
    if (this.getAuthResponse.login && this.getAuthResponse.ident_method === `PHONE`) {
      this.login = this.getAuthResponse.login;
    }
    this.showFakeInput = true;
    this.$nextTick()
      .then(() => {
        this.$refs.fakeInput.focus();
        return delay(500);
      })
      .then(() => {
        this.$refs.loginInput.focus();
        this.showFakeInput = false;
      });
  },

}
</script>

The problem arises in this mutation - this.startCodeTimer (30);

Mutation file:

export default {
  setAuthResponse(state, data) {
    state.authResponse = data
  },
  setCodeResponse(state, data) {
    state.codeResponse = data
  },
  setRegResponse(state, data) {
    state.regResponse = data
  },
  setAuthCode(state, data) {
    state.authCode = data
  },
  startCodeTimer(state, time) {
    state.newCodeTimer = time
    state.codeTimerId = setInterval(() => {
      if (state.newCodeTimer) {
        state.newCodeTimer--
      } else {
        clearInterval(state.codeTimerId)
      }
    }, 1000)
  },
  resetCodeTimer(state) {
    state.newCodeTimer = 0
  }
}

If I understand correctly, then the problem is here.

state.codeTimerId = setInterval(() => {
      if (state.newCodeTimer) {
        state.newCodeTimer--
      } else {
        clearInterval(state.codeTimerId)
      }
    }, 1000)

But so far there are no ideas how to solve it.

CodePudding user response:

The problem was that state cannot be changed inside setInterval. Solution: Create a mutation that will change the state and call this mutation inside setInterval.

Example:

 setNewCode(state, count) {
    state.newCodeTimer = count
  },
  startCodeTimer(state, time) {
    state.newCodeTimer = time
    state.codeTimerId = setInterval(() => {
      if (state.newCodeTimer) {
        this.commit('authorization/setNewCode', time--)
      } else {
        clearInterval(state.codeTimerId)
      }
    }, 1000)
  },
  • Related