Svelte 组件在一个插槽中不会看到道具更新,但如果在另一个插槽中会 [英] Svelte component does not see prop update if in one slot, but does if in another slot

查看:36
本文介绍了Svelte 组件在一个插槽中不会看到道具更新,但如果在另一个插槽中会的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在以下组件(称为注释屏幕")中,组件 DocPicker 让用户设置变量 selectedDoc,该变量又应该更新两个组件:FormSearchResultView.

<屏幕绑定:屏幕><svelte:fragment slot="sidebar"><DocPicker bind:picked={selectedDoc} bind:isOpen={isPickerOpen}/>{#if selectedDoc}<表单文档={selectedDoc}/>{/如果}</svelte:fragment><svelte:fragment slot="content">{#if selectedDoc}<SearchResultView doc={selectedDoc} on:close={() =>{selectedDoc = undefined}}/>{/如果}</svelte:fragment></屏幕>

问题是selectedDoc 改变时Form 组件没有更新.第一次 selectedDocundefined 到某个值,组件 Form 出现正确的值,但如果以后 selectedDoc> 更改(因为用户使用了 DocPicker),组件 Form 保持以前的值.

SearchResultView 每次都会更新,就像一个魅力,所以我虽然问题来自 Form 组件,但如果我复制行 <Form doc={selectedDoc}/> 紧挨着 SearchResultView 所在的位置(即,在 ),Form 的这个实例可以正常工作!同样,如果我在 <svelte:fragment slot="sidebar"> 中放入另一个 SearchResultView 实例,这个 SearchResultView 实例会不更新.

所以好像跟组件插入的where有关,也就是Screen组件的哪个slot.但是 Screen 组件非常简单,归结为:

<div class="screen-sidebar"><slot name="sidebar"></slot>

</侧边栏><slot name="content"></slot>

Sidebar 组件也很简单:

解决方案

好的,明白了.事实证明,问题的原因并不在我提出的问题中的代码片段中.我在 Svelte 的 DocPicker 组件中做了一些你可能根本不应该做的事情.Svelte 编译器没有抱怨,但显然真的很困惑,这导致了我在问题中描述的奇怪行为.

这里是问题的详细信息以及我是如何发现它的.

我试图想出一个最小的工作示例,这样我就可以把它放在一个 Svelte REPL 中,当然问题不是重现.

所以我尝试了许多中间步骤,从我的问题情况到失败的最小工作示例,看看哪个精确步骤会带回错误.将它带回来的是DocPicker 组件中的bind:isOpen={isPickerOpen} 属性绑定.所以问题很可能与这个组件有关.

我查看了这个 DocPicker 组件的代码,并且以一种奇怪的方式使用了 prop isOpen.我什至留下了关于它的评论警告:

export let isOpen: boolean = true;$: isOpen = !Boolean(picked);//XXX 是强制"的好习惯吗?一个也是被动赋值的变量?函数 openPicker() {isOpen = true;}

所以我想我现在可以在评论中回答这个问题:不,这可能不是一个好主意.或者也许不是这样.如果它总是一个坏主意,那么 Svelte 编译器在看到这个时不能引发错误吗?

无论如何,我改变了 isOpen 的管理方式(不是通过响应式赋值,只通过命令式赋值),现在它就像一个魅力.

In the following component (call it "annotation screen"), component DocPicker let the user set the variable selectedDoc, which in turn is supposed to update two components: Form and SearchResultView.

<script lang="ts">
  import type { AppScreen, QuestionGouv } from '../../types.js';
  import Screen from '../../Screen.svelte';
  import DocPicker from './DocPicker/DocPicker.svelte';
  import SearchResultView from '../../SearchResultView.svelte';
  import Form from './Form.svelte';

  export let screen: AppScreen;
  
  let selectedDoc: QuestionGouv;
  let isPickerOpen: boolean;
</script>

<Screen bind:screen>
  <svelte:fragment slot="sidebar">
    <DocPicker bind:picked={selectedDoc} bind:isOpen={isPickerOpen}/>
    {#if selectedDoc}
      <Form doc={selectedDoc}/>
    {/if}
  </svelte:fragment>
  <svelte:fragment slot="content">
    {#if selectedDoc}
      <SearchResultView doc={selectedDoc} on:close={() => {selectedDoc = undefined}}/>
    {/if}
  </svelte:fragment>
</Screen>

The problem is that the Form component is not updated when selectedDoc changes. The first time selectedDoc goes from undefined to some value, component Form appears with the proper value, but if later selectedDoc changes (because the user used DocPicker), component Form stays with the previous value.

SearchResultView is updated every time though, works like a charm, so I though the problem is coming from the Form component instead, but if I copy line <Form doc={selectedDoc}/> right next to where SearchResultView is (that is, in the <svelte:fragment slot="content">), this instance of Form works without a problem! Similarly, if I put another instance of SearchResultView in <svelte:fragment slot="sidebar">, this instance of SearchResultView does not update.

So it seems to be related to where the component is inserted, namely, in which slot of the Screen component. But the Screen component is pretty trivial, it boils down to:

<Sidebar>
  <div class="screen-sidebar">
    <slot name="sidebar"></slot>
  </div>
</Sidebar>
<slot name="content"></slot>

And the Sidebar component is trivial as well:

<div class="sidebar">
  <slot></slot>
</div>

解决方案

Okay, got it. It turned out that the cause of the problem was not in the code snippets that I put in my question. I did something you are probably not supposed to do at all in Svelte in the DocPicker component. The Svelte compiler did not complain but got really confused apparently, and this resulted in the weird behavior I describe in the question.

Here are the details of the problem and how I found it.

I tried to come up with a minimal working example of the situation so that I could put it in a Svelte REPL, and of course the problem was not reproducing.

So I tried many intermediate steps from my problematic situation to the failed minimal working example, to see which precise step would brings back the bug. And what was bringing it back was the bind:isOpen={isPickerOpen} prop binding in the DocPicker component. So the problem is probably linked to this component.

I had a look in the code of this DocPicker component, and prop isOpen is used in a weird way. I had even left a comment warning about it:

export let isOpen: boolean = true;
$: isOpen = !Boolean(picked);
// XXX is it good practice to "force" a variable that is also reactively assigned?
function openPicker() {
  isOpen = true;
}

So guess I now have my answer to the question in the comment: no, it's probably not a good idea. Or maybe not this way. If it's always a bad idea, couldn't the Svelte compiler raise an error when it sees this?

Anyway, I changed the way isOpen is managed (not through reactive assignments, only imperative ones) and now it works like a charm.

这篇关于Svelte 组件在一个插槽中不会看到道具更新,但如果在另一个插槽中会的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆