0
votes
<template>
  <div class="counter-warp">
    <p>make</p>
    <div class="statusCon">
      <p class="card">
        last: {{completed.length > 0 ? completed[completed.length - 1].text : ''}}
      </p>
      <p>completed: {{completed.length}} </p>
      <button @click="showAllSentences">showall</button>
    </div>
    <input v-model="currentText"/>
    <p>remain: {{wordsRemain}}</p>
    <button @click="addSentence">Next</button>

    <a href="/pages/index/main" class="home">home</a>
  </div>
</template>

<script>
// Use Vuex
import { mapGetters } from 'vuex';
import wxapi from '@/lib/wxapi';

export default {
  components: {
  },
  computed: {
    ...mapGetters(['wordsRemain', 'completed']),
    currentText: {
      get() {
        return this.$store.state.make.current.text;
      },
      set(val) {
        this.$store.commit('setText', val);
      },
    },
  },
  methods: {
    addSentence() {
      this.$store.commit('addSentence', this.$store.state.make.current);
    },
    complete() {
      this.$store.dispatch('complete').then((workId) => {
        wxapi.navigateTo({ url: `/pages/index/main?id=${workId}` });
      });
    },
  },
};

</script>
<style>
.counter-warp {
  text-align: center;
  margin-top: 100px;
}
.home {
  display: inline-block;
  margin: 100px auto;
  padding: 5px 10px;
  color: blue;
  border: 1px solid blue;
}

</style>

// store.js
// https://vuex.vuejs.org/zh-cn/intro.html
// make sure to call Vue.use(Vuex) if using a module system
import Sentence from '@/lib/Sentence';
import Constants from '@/lib/Constants';
import { Work, User } from '@/lib';


// 
const ROW_LENGTH = {
  portrait: Constants.WINDOW_WIDTH,
  landscape: Constants.WINDOW_HEIGHT,
};


const store = {
  state: {
    orientation: 'portrait',
    current: new Sentence(),
    sentences: [],
  },
  getters: {
    completed: state => state.sentences,
    sentencesLength: state => state.sentences.length,
    wordsRemain: (state) => {
      const fontSize = state.current.fontSize;
      const marginTextLength = Constants.MARGIN * fontSize;
      const remainPx = ROW_LENGTH[state.orientation] - marginTextLength;
      const textLimit = Math.floor(remainPx / fontSize);
      return textLimit - state.current.text.length;
    },
    // 
    lastSentence: (state, getters) => {
      const obj = state;
      return obj.sentences[getters.sentencesLength - 1];
    },
  },
  mutations: {
    addSentence: (state, sentence) => {
      state.sentences.push(sentence);
      state.current = new Sentence();
      console.log(state);
    },
    setText: (state, text) => {
      state.current.text = text;
    },
  },
  actions: {
    complete: ({ state, commit }) => {
      // commit('setText',)
      // commit('addSentence', state.current);
      const config = Object.assign({}, state);
      commit('setConfig', config);
      const work = new Work();
      work.set('author', User.current);
      work.set('config', config);
      return work.save().then(obj => Promise.resolve(obj.id), (err) => {
        console.log(err);
      });
    },
  },
};

export default store;

When I click the 'next' button,the 'addSentence' mutation handler should be called,but vue prompt me that'[vuex] Do not mutate vuex store state outside mutation handlers.

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

Sentence is a simple class like { text:'' ,sth: {}};.

1
is it possible for you to recreate the problem on codesandbox? codesandbox.io/s/vueA. L
I can't reproduce it: codesandbox.io/s/jjm4qyoy1wacdcjunior

1 Answers

0
votes

I think the problem is within the component method:

methods: {
    addSentence() {
        this.$store.commit('addSentence', this.$store.state.make.current);
    },
    complete() {
        this.$store.dispatch('complete').then((workId) => {
            wxapi.navigateTo({ url: `/pages/index/main?id=${workId}` });
        });
    }   ,
},

When you call this.$store.state.make.current, this is modifying the store directly. Rather than adding a payload to addSentence(), all you need to do is get the store to create the current Sentence directly within the mutation.

By the way, you should not call commit directly from the component. You need to call a dispatch action on the store and then commit the mutation via that.

The pattern would be:

COMPONENT:

methods: {
    addSentence () {
        this.$store.dispatch('attemptAddSentence');
    },

STORE:

actions: {
    attemptAddSentence (({state, commit}) => {
        return new Promise((resolve, reject) => {
            commit(‘addSentence’)
            resolve()
        })
    }),

mutations: {
    addSentence (state) => {
        // Build sentence here...
    }),

Hope that helps.