I'm building a tic tac toe game in Vue and I'm getting this error when the I try to reset the board.
"[Vue warn]: Duplicate keys found during update: " Make sure keys are unique."
The board starts to distort visually. I get extra squares appearing on the screen.
What am I missing? Is my state set up correctly?
<template>
<h2>{{ msg }}</h2>
<h2 v-if="winner" >Winner: {{ winner }}</h2>
<h2 v-else>Player Move: {{ turn }}</h2>
<div id="board">
<div v-for="(square, index) in squares" :key="square" @click="takeTurn(index)">
{{ square }}
</div>
</div>
<button @click="reset">
Reset
</button>
</template>
<script>
import { ref, computed } from 'vue'
export default {
name: 'MainBoard',
props: {
msg: String // Vue has prop typechecking by default
},
setup () {
const turn = ref('X') // player first turn
const squares = ref([])
for (let i = 0; i <= 8; i ) squares.value.push('') // make a blank board}
console.log('squares value: ' squares.value)
const winner = computed(() => checkForWinner(squares.value.flat())) // check 4 winner everytime board changes
console.log('computed:' squares.value.flat())
function checkForWinner (value) {
console.log('check4Winner: ' value)
// NOTE: in this version we have a
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8], // horizontal wins
[0, 3, 6],
[1, 4, 7],
[2, 5, 8], // vertical wins
[0, 4, 8],
[2, 4, 6] // cross wins
]
for (let i = 0; i < lines.length; i ) {
const [a, b, c] = lines[i]
if (
value[a] &&
value[a] === value[b] &&
value[a] === value[c]
) {
console.log('value[a]: ' value[a])
return value[a]
}
}
return null
}
function checkForTieGame () {
// make it a boolean
// let isTie = true
// check each space on the board
squares.value.forEach((s) => {
// if a space is available
// if (s === null) isTie = false
})
// if tie is true, end the game
// if (isTie === true) gameOver.value = true
}
function takeTurn (n) {
console.log('takeTurn value: ' n)
if (winner.value) return
squares.value[n] = turn.value
checkForTieGame()
turn.value = turn.value === 'X' ? 'O' : 'X'
console.log('player: ' turn.value)
}
function reset () {
turn.value = 'O'
console.log(squares.value.length)
squares.value = squares.value.map((square) => '')
// for (let i = 0; i <= 8; i ) squares.value.push('') // make a blank board
}
return { squares, turn, winner, checkForWinner, takeTurn, reset }
}
}
</script>
<style scoped>
button{
margin-top: 40px;
}
#board {
border: 1px solid darkred;
height: 400px;
width: 400px;
margin:0 auto;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.square {
background: #fff;
border: 1px solid #999;
/*float: left;*/
font-size: 70px;
font-weight: bold;
line-height: 34px;
height: 100px;
width: 100px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
}
</style>
Thank you for your time
CodePudding user response:
Set key in your v-for loop to be unique(index) , so replace :key="square"
with :key="index"
const { ref, computed } = Vue
const app = Vue.createApp({
props: {
msg: String // Vue has prop typechecking by default
},
setup () {
const turn = ref('X') // player first turn
const squares = ref([])
for (let i = 0; i <= 8; i ) squares.value.push('') // make a blank board}
console.log('squares value: ' squares.value)
const winner = computed(() => checkForWinner(squares.value.flat())) // check 4 winner everytime board changes
console.log('computed:' squares.value.flat())
function checkForWinner (value) {
console.log('check4Winner: ' value)
// NOTE: in this version we have a
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8], // horizontal wins
[0, 3, 6],
[1, 4, 7],
[2, 5, 8], // vertical wins
[0, 4, 8],
[2, 4, 6] // cross wins
]
for (let i = 0; i < lines.length; i ) {
const [a, b, c] = lines[i]
if (
value[a] &&
value[a] === value[b] &&
value[a] === value[c]
) {
console.log('value[a]: ' value[a])
return value[a]
}
}
return null
}
function checkForTieGame () {
// make it a boolean
// let isTie = true
// check each space on the board
squares.value.forEach((s) => {
// if a space is available
// if (s === null) isTie = false
})
// if tie is true, end the game
// if (isTie === true) gameOver.value = true
}
function takeTurn (n) {
console.log('takeTurn value: ' n)
if (winner.value) return
squares.value[n] = turn.value
checkForTieGame()
turn.value = turn.value === 'X' ? 'O' : 'X'
console.log('player: ' turn.value)
}
function reset () {
turn.value = 'O'
squares.value = Array(9).fill('');
}
return { squares, turn, winner, checkForWinner, takeTurn, reset }
}
})
app.mount('#demo')
button{
margin-top: 40px;
}
#board {
border: 1px solid darkred;
height: 200px;
width: 200px;
margin:0 auto;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.square {
background: #fff;
border: 1px solid #999;
/*float: left;*/
font-size: 70px;
font-weight: bold;
line-height: 34px;
height: 50px;
width: 50px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
}
<script src="https://unpkg.com/[email protected]/dist/vue.global.prod.js"></script>
<div id="demo">
<h2>{{ msg }}</h2>
<h2 v-if="winner" >Winner: {{ winner }}</h2>
<h2 v-else>Player Move: {{ turn }}</h2>
<div id="board">
<!--