Svelte.js - 如何使用新道具重新渲染子组件?

alw*_*ing 13 javascript forms state svelte

我正在使用 Svelte.js 创建一个多步表单,但我遇到了一个问题,用独特的道具呈现每个表单页面。

这是一个简单的演示,向您展示我的意思:

// App.svelte

<script>
    import FormPage from "./FormPage.svelte";
    let formNode;

    let pageSelected = 0;

    let formPages = [
        {
            name: "email",
            label: "Email"
        },
        {
            name: "password",
            label: "Password"
        }
    ];

    const handleIncPage = () => {
        if(pageSelected + 1 < formPages.length)
        pageSelected = pageSelected + 1;        
    }

    const handleDecPage = () => {
        if(pageSelected -1 > -1)
        pageSelected = pageSelected - 1;        
    }
</script>

<form bind:this={formNode}>
    <FormPage pageData={formPages[pageSelected]} />
</form>

<button on:click={handleDecPage}>Back</button>
<button on:click={handleIncPage}>Next</button>

<p>
    Page Selected: {pageSelected}
</p>
Run Code Online (Sandbox Code Playgroud)

这是FormPage组件:

// FormPage.svelte

<script>
    export let pageData;

    const {name, label} = pageData;
</script>

<div id={`form-page-${name}`}>
    <label>Label: {label}</label>
    <input type="text" name={name} id={`input-${name}`} />
</div>

<pre>{JSON.stringify(pageData, null, 2)}</pre>
Run Code Online (Sandbox Code Playgroud)

当我运行应用程序和 inc/dec 时pageSelectedpageData道具成功更改 - 如pre元素中所示。但是,labelinput元素与它们在第一页上的完全相同。该id包装的div元素和id的的input元素也保持不变。

当我输入input并更改页面时,文本保持不变。

我的目标是:重新呈现的FormPage组件时pageSelected的变化,并具有inputlabel修改基于这些新道具它们的值。也应该是这样,当我更改页面时,已经输入的文本input应该更新并且为空(因为没有为输入提供初始值)。

在 React.js 中完成了多步表单后,我将使用一个独特的key属性来确保FormPage每次pageSelected状态改变时我的重新渲染。但我不确定如何在 Svelte.js 中做类似的事情。

有什么建议?

更新:

在StackOverflow 上阅读了这个问题后,我发现了如何让inputlabel元素以及id属性发生变化。这是我更新的FormPage组件:

// FormPage.svelte

<script>
    export let pageData;

    $: name = pageData.name;
    $: label = pageData.label;

</script>

<div id={`form-page-${name}`}>
    <label>Label: {label}</label>
    <input type="text" name={name} id={`input-${name}`}  />
</div>

<pre>{JSON.stringify(pageData, null, 2)}</pre>
Run Code Online (Sandbox Code Playgroud)

但是,里面的文字input仍然没有改变。

有没有办法也更新输入的值?对于我的用例来说,每次 props 更改时创建一个全新的元素是理想的,但 Svelte 似乎只更新在同一底层 DOM 节点上发生更改的少数属性。

在这种情况下,可以告诉 Svelte 重新创建元素吗?

更新 2

所以我想出了一种方法来改变输入的值。这是我更新的FormPage组件:

// FormPage.svelte

<script>
    export let pageData;

        import {onMount, afterUpdate, onDestroy} from "svelte";

        let inputNode;

        $: name = pageData.name;
        $: label = pageData.label;
        $: value = pageData.initialValue || "";

        onMount(() => {
            console.log("Mounted");
        });

        afterUpdate(() => {
            console.log("Updated");
            inputNode.value = value
        });

        onDestroy(() => {
            console.log("Destroyed");
        });

</script>

<div id={`form-page-${name}`}>
    <label>Label: {label}</label>
    <input type="text" name={name} id={`input-${name}`} bind:this={inputNode}  />
</div>

<pre>{JSON.stringify(pageData, null, 2)}</pre>
Run Code Online (Sandbox Code Playgroud)

这解决了更新输入值的直接问题。

我添加了onMount,afterUpdateonDestroy函数来查看组件如何随时间变化。

首先onDestroy调用函数,然后调用onMount。这两个都只触发一次。但是,afterUpdate每次道具更改时都会触发。这证实了我的怀疑,即该组件没有被重新创建。

chm*_*seo 6

{#key}..{/key}解决了问题。

在我的情况下,Sizing组件用于更新大小(字体大小或行高等),就像您的一样FormPage.svelte

<script>
import Sizing from '..../Sizing.svelte'

const pages = {
  'font-size': Sizing,
  'line-height': Sizing, 
  ...
}
const props = {
  'font-size': { min: 8, max: 20, step: 1},
  'line-height': {min: 1, max: 3, step: 0.1}
}
let currentProp // 'font-size', 'line-height' etc ..
</script>

<div>
  {#key currentProp}
     <svelte:component this="{pages[currentProp]}" {...props}/>
  {/key}
</div>
Run Code Online (Sandbox Code Playgroud)

每当currentProp发生变化时,Sizing 组件也会更新。

{...props}映射到导出变量

// Sizing.svelte
<script>
  export let min
  export let max
  export let step
</script>
Run Code Online (Sandbox Code Playgroud)


nas*_*h11 5

有一种更简单的方法可以做到这一点。将pageData在您的更新FormPage,问题是当你使用const {name, label} = pageData;,你基本上是分配的值namelabel参数在pageData两个常量,namelabel分别。这两个变量不再绑定到父组件。为了解决这个问题,pageData直接在FormPage下面使用。

FormPage.svelte

<script>
    export let pageData;
</script>

<div id={`form-page-${pageData.name}`}>
    <label>Label: {pageData.label}</label>
    <input type="text" name={pageData.name} bind:value={pageData.value} id={`input-${pageData.name}`} />
</div>
Run Code Online (Sandbox Code Playgroud)

注意:如果您不希望编译器在传递undefinedno 时给您一个错误pageData,那么您可以像这样初始化pageData为一个空对象。{}export let pageData = {};

至于input问题,最容易做的事情是添加valueformPages,然后bindvaluepageData.value如上所示FormPage

formPages数据

<script>
    export let pageData;
</script>

<div id={`form-page-${pageData.name}`}>
    <label>Label: {pageData.label}</label>
    <input type="text" name={pageData.name} bind:value={pageData.value} id={`input-${pageData.name}`} />
</div>
Run Code Online (Sandbox Code Playgroud)

这是一个关于REPL的工作示例。