Nick Synev
Nick Synev

Reputation: 387

Vuex store does not update component collection when pushing from mutation

I'm trying to push task to state.tasks variable array in store module via mutation. When I'm fetching all tasks and assign it to a state.tasks everything works perfect and appears on the screen, but when I'm pushing task to a state.tasks store is not adding it on a view.(I checked it via console.log task was pushed to state.tasks in mutation. No errors). I've left info below. What am I doing wrong?

State:

const state = {
    tasks: [],
};

Actions: The tasks is api service which I import import tasks from '../../api/tasks';

const actions = {
    getTasks({commit}) {
        return new Promise((resolve, reject) => {
            tasks.getTasks().then(response => {
                commit('setTasks', response.data.data);
                resolve(response);
            }).catch(error => {
                reject(error);
            })
        });
    },

    createTask({commit}, text) {
        return new Promise((resolve, reject) => {
            tasks.createTask(text).then(response => {
                commit('createTask', response.data.data);
                resolve(response);
            }).catch(error => {
                reject(error);
            })
        });
    }
};

Mutation:

const mutations = {
    setTasks(state, tasks) {
        state.tasks = tasks;
    },
    createTask(state, task) {
        state.tasks.push(task);
        console.log(state.tasks);
    }
};

Getters:

const getters = {
    undoneTasks(state) {
        return state.tasks.filter(task => task.done == false);
    },
    doneTasks(state) {
        return state.tasks.filter(task => task.done == true);
    }
};

In component:

mounted() {
            store.dispatch('getTasks').catch(error => {
                if (error.response) {
                    this.checkResponse(error.response.status);
                }
            });
        },

methods: {
        createTask() {
            store.dispatch('createTask', this.taskText)
                .then(() => {
                    this.taskText = '';
                }).catch(error => {
                if (error.response) {
                    this.checkResponse(error.response.status);
                }
            });
        }
 },
computed: {
            ...mapGetters({
                tasks: 'undoneTasks'
            }),
        },

Updated

In module actions tasks is api service which I import import tasks from '../../api/tasks'; Also I tried to state.tasks.pop() and It worked, I thought that is because of incorrect incoming data, but I checked again and it really pushing it to state.tasks but component does not react...

Upvotes: 4

Views: 1871

Answers (2)

Nick Synev
Nick Synev

Reputation: 387

Ok, guys so I figured it out. Thanks for all support and answers. So I have tried manually set object for push in mutation.

createTask(state, task) {
        const taskObj = {id: task.id, text: task.text, done: false};
        state.tasks.push(taskObj);
    }

And It worked, the problem was because of done field that was coming as null from server. I think I should read more info about vuex, but it is very strange...

Upvotes: 1

Stephen Gilboy
Stephen Gilboy

Reputation: 5825

I would set tasks as an empty array to start with and only allow the createTask mutation to add tasks to the array. I've recreated your code and it works fine, though I am not using the spread mapGetters.

const store = new Vuex.Store({
	state: {
  	tasks: [],
  },
  actions: {
  	createTask({commit}, task) {
    	return new Promise((res, rej) => {
      	setTimeout(() => {
        	commit('createTask', task);
          res(true);
        }, 1000);
      });
    },
  },
  mutations: {
  	createTask(state, task) {
    	state.tasks.push(task);
    },
  },
  getters: {
  	undoneTasks: state => state.tasks.filter(t => t.done === false),
  },
});

new Vue({
  el: "#app",
  store,
  data: {
  },
  methods: {
  	toggle: function(todo){
    	todo.done = !todo.done
    }
  },
  computed: {
  	tasks() {
    	return this.$store.getters.undoneTasks;
    }
  },
  created() {
  	this.$store.dispatch('createTask', { text: "Learn JavaScript", done: false });
    this.$store.dispatch('createTask', { text: "Learn Vue", done: false });
    setTimeout(() => 
  	[{ text: "Play around in JSFiddle", done: false },
     { text: "Build something awesome", done: false }].map(t => this.$store.dispatch('createTask', t)), 1000);
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
  <h2>Todos:</h2>
  <ol>
    <li v-for="todo in tasks">
      <label>
        <input type="checkbox"
          v-on:change="toggle(todo)"
          v-bind:checked="todo.done">

        <del v-if="todo.done">
          {{ todo.text }}
        </del>
        <span v-else>
          {{ todo.text }}
        </span>
      </label>
    </li>
  </ol>
</div>

Upvotes: 0

Related Questions