I'm new to Svelte and experimenting with a trivial Tic Tac Toe implementation.
I'm confused why reactivity is working when I update the elements of an array, contrary to the documentation which says that its necessary to assign the array to itself to trigger subscription notifications.
I can't setup a REPL for the application as I'm using Typescript, the Svelte REPL seems to only support JS, so I'm hoping that this main Board.svelte file is sufficient, as this seems to be where the issue lies.
The question I have relates to the tiles
array, which is updated in a click handler. Note that the click handler is not reactive (no $:
and as incline function rather than an arrow function), but modifications to tiles[index]
seem to cause reactivity, because the UI updates, and so also does the gameOver
variable which is computed whenever the board changes (it returns either undefined
if the game is in progress, or an object containing the winning player and the tile indexes that comprise the win).
Note that the isWin
function is written with reactivity - if I remove the $:
it stops updating the isWin
prop on the tile, which controls whether the tile is highlighted on game over.
This seems to be contradictory - gameOver
needs tiles
passing as an argument and to be marked reactive or it won't react on tile updates (as I would expect). isWin
needs to be reactive, or it won't cause Tile
to re-render.
The but I can't explain is why click
managed to cause updates to be fired, when the array itself is not updating (only the element). NB: Removing references to gameOver
and activePlayer
don't make any difference to subscription notifications on tiles
.
I specifically avoided using svelte/stores in this implementation. I got in a mess trying to use them and stripped it back to basic reactivity and found that did everything I needed, and I'm not clear if the 'array = array' convention is different when using stores vs reactive vars.
I'd really like to understand this better before moving forward.
<script lang="ts">
import Tile from './Tile.svelte'
import Result from "./Result.svelte"
import Reset from "./Reset.svelte"
import type { GameOver, Player } from "./types"
import { getGameOver } from "./getGameOver"
let activePlayer: Player = 'X'
const tiles: Array<Player | undefined> = Array(9).fill(undefined)
let gameOver: GameOver | undefined
$: gameOver = getGameOver(tiles)
let isWin: (index: number) => boolean
$: isWin = (index: number) => {
return gameOver?.winLine?.includes(index) ?? false
}
function click(index: number) {
if (!gameOver && tiles[index] === undefined) {
tiles[index] = activePlayer
activePlayer = activePlayer === 'X' ? 'O' : 'X'
}
}
function reset() {
for (const index in tiles) {
tiles[index] = undefined
}
activePlayer = 'X'
}
</script>
<div class:gameOver={!!gameOver}>
<Result gameOver={gameOver} activePlayer={activePlayer}/>
<div >
{#each tiles as tile, i}
<Tile player={tile} isWin={isWin(i)} click={() => click(i)}/>
{/each}
</div>
<Reset reset={reset}/>
</div>
<style>
.tiles {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(3, 100px);
}
</style>
CodePudding user response:
Assigning an element of an array is just like assigning any other object property, so the array will be marked as dirty.
What the documentation is warning about is the use of functions which internally change the array, e.g. push
or pop
. This would require Svelte to know what the various functions do to keep reactivity.
Svelte analyzes the code to see where modifications of state happen, e.g.
<script>
let items = ['a', 'b', 'c'];
</script>
<button on:click={() => items[0] = '!'}>
Change Item 0
</button>
Here the compiled code for the click handler will contain additional logic to invalidate items
, because its property '0'
has been modified.
function instance($$self, $$props, $$invalidate) {
let items = ['a', 'b', 'c'];
const click_handler = () => $$invalidate(0, items[0] = '!', items);
return [items, click_handler];
}
This happens regardless of whether the object is an array or not, or whether additional nested properties are added.