Search code examples
javascriptvue.jsvuejs2vuexcomputed-properties

update item in two dimensional array within the Vuex store


I would like to get into VueJs development and created a simple Minesweeper game.The two dimensional grid is managed by a Vuex state. When clicking on a cell I would like to reveal it so my current code is

  [MutationTypes.REVEAL_CELL]: (state, { rowIndex, columnIndex }) => {
    state.board[rowIndex][columnIndex].isRevealed = true;
  }

Unfortunately this has no affect to the UI. This problem is known and described here

https://v2.vuejs.org/v2/guide/list.html#Caveats

The docs told me to use something like this

import Vue from "vue";

  [MutationTypes.REVEAL_CELL]: (state, { rowIndex, columnIndex }) => {
    const updatedCell = state.board[rowIndex][columnIndex];
    updatedCell.isRevealed = true;

    Vue.set(state.board[rowIndex], columnIndex, updatedCell);
    Vue.set(state.board, rowIndex, state.board[rowIndex]);
  }

but it did not help. Lastly I tried to create a copy of the board, modify the values and assign that copy to the board.

  [MutationTypes.REVEAL_CELL]: (state, { rowIndex, columnIndex }) => {
    const newBoard = state.board.map((row, mapRowIndex) => {
      return row.map((cell, cellIndex) => {
        if (mapRowIndex === rowIndex && cellIndex === columnIndex) {
          cell = { ...cell, isRevealed: true };
        }
        return cell;
      });
    });

    state.board = newBoard;
  }

This didn't work neither. Does someone got an idea?

I created a Codesandbox showing my project

https://codesandbox.io/s/vuetify-vuex-and-vuerouter-d4q2b

but I think the only relevant file is /store/gameBoard/mutations.js and the function REVEAL_CELL


Solution

  • The problem is in Cell.vue and the issue is that you're checking an unchanging variable to determine the state of reveal. You've abstracted this.cell.isRevealed into a variable called isUnrevealed which is never told how to change after the initial load.

    Option 1

    isUnrevealed seems like an unnecessary convenience variable. If you get rid of isUnrevealed and change the references to it to !cell.isRevealed, the code will work as expected.

    Option 2

    If you're set on using this variable, change it to a computed so that it constantly updates itself whenever the Vuex state propagates a change to the cell isRevealed prop:

    computed: {
      isUnrevealed() {
        return !this.cell.isRevealed;
      }
    }
    

    If you go this route, don't forget to remove the property from data and remove the assignment in mounted (first line).


    You'll also have the same problem with isMine and cellStyle. So, completely remove data and mounted and make them both computed as well.

    computed: {
      isMine() {
        return this.cell.isMine;
      },
      cellStyle() {
        if (!this.cell.isRevealed) {
          return "unrevealedCell";
        } else {
          if (this.isMine) {
            return "mineCell";
          } else {
            let neighbourCountStyle = "";
            ... // Switch statement
            return `neutralCell ${neighbourCountStyle}`;
          }
        }
      }
    }