首页 > 解决方案 > 当 Svelte 中的道具发生变化时如何刷新 DOM?

问题描述

我有一个类似网格的结构,其中有一个来自父级的指针对象。现在通过一些操作,指针正在更新,但更改没有反映在子组件中,DOM 也没有更新。

家长:

<script>
 import Boxes from "./Boxes.svelte";

 let boxes = [
   ["", "", "", "", "", "", "", ""],
   ["", "", "", "", "", "", "", ""],
   ["", "", "", "", "", "", "", ""],
   ["", "", "", "", "", "", "", ""],
   ["", "", "", "", "", "", "", ""],
   ["", "", "", "", "", "", "", ""],
   ["", "", "", "", "", "", "", ""],
   ["", "", "", "", "", "", "", ""]
 ];

 let activeBox = {
   x: 0,
   y: 0
 };

 function handleKeydown(keyEvent) {
   let i = activeBox.x;
   let j = activeBox.y;

   const width = boxes[i].length,
     height = boxes.length,
     left = 37,
     up = 38,
     right = 39,
     down = 40,
     tab = 9,
     backspace = 8;



   // Loop around single row with right and left arrows
   if (keyEvent.keyCode == right) {
     activeBox.x = activeBox.x + 1;
     if(activeBox.x === boxes[i].length) activeBox.x = 0;
     return;
   }

   $: console.log("^^^ activeBox &&", activeBox);
</script>

<style>
 main {
   display: flex;
   align-items: center;
   justify-content: center;
 }

 @media (min-width: 640px) {
   main {
     max-width: none;
   }
 }
</style>

<svelte:window on:keydown="{handleKeydown}" />
<main>
 <Boxes boxes={boxes} activeBox={activeBox} />
</main>

子组件正在使用 activeBox 变量来呈现选定的框。但这似乎不起作用,因为虽然第一个渲染效果很好,但盒子并没有得到更新。

孩子:

<script>
  export let boxes;
  export let activeBox;

  $: console.log("^^^ active Box updated");

  function getSelectedClass(i, j) {
    if (activeBox.x === j && activeBox.y === i) {
      return "selected";
    }
    return "";
  }
</script>

<style>
  .grid-container {
    display: grid;
    grid-template-columns: auto auto auto auto auto auto auto auto;
    background-color: #2196f3;
    padding: 0px;
  }
  .grid-item {
    background-color: rgba(255, 255, 255, 0.8);
    border: 1px solid rgba(0, 0, 0, 0.8);
    width: 40px;
    height: 40px;
    padding: 20px;
    font-size: 30px;
    text-align: center;
  }
  .selected {
    border: 1px solid red;
  }
</style>

<main>
  <div class="grid-container">
    {#each boxes as row, i}
      {#each row as column, j}
        <div class=" grid-item {getSelectedClass(i, j)}">{boxes[i][j]}</div>
      {/each}
    {/each}
  </div>
</main>

我真的可以使用一些洞察力,因为我还没有正确掌握 Svelte 的概念。任何帮助将非常感激。

标签: svelte

解决方案


使用您的代码, 的值activeBox实际上传播给了孩子,但您看不到它,因为没有反应性代码在孩子中使用它。

当它所依赖的变量的值发生变化时,反应性代码会重新执行和重新评估,但仅适用于反应性代码中立即存在的变量。

所以,在这段代码中:

  $: console.log("^^^ active Box updated");

...根本没有变量。所以这个块最初会运行一次,然后再也不运行。

这个:

    {#each boxes as row, i}
      ...
    {/each}

boxes每当变量发生变化时都会重新评估。

在这个:

        <div class=" grid-item {getSelectedClass(i, j)}">...</div>

...反应值仅{getSelectedClass(i, j)}“看到”变量getSelectedClass和。并且是局部变量,因此 Svelte 不会跟踪它们。一个顶级变量,因此如果它的值发生变化(即),它将被跟踪并重新评估块,但这里从来没有这种情况。ijijgetSelectedClass getSelectedClass = 'foo'

那么...如何解决?

只需activeBox在反应表达式中添加 ,编译器就会在其值更改时重新计算它(即使它实际上并未在函数中使用)。因此,这将按原样修复您的代码:

        <div class=" grid-item {getSelectedClass(i, j, activeBox)}">...</div>

另一种选择是使用基于实际包含activeBox变量的表达式的另一个构造:

        <div class="grid-item" class:selected={activeBox.x === j && activeBox.y === i}>{boxes[i][j]}</div>

这是一个中间立场:

<script>
  // NOTE this block contains activeBox variable, so isSelected is recreated when
  // activeBox changes
  $: isSelected = (i, j) => activeBox.x === j && activeBox.y === i
</script>

<main>
  <div class="grid-container">
    {#each boxes as row, i}
      {#each row as column, j}
        <div class="grid-item" class:selected={isSelected(i, j)}>{boxes[i][j]}</div>
      {/each}
    {/each}
  </div>
</main>

推荐阅读