Svelte.js - 如何使用新道具重新渲染子组件?
Posted
技术标签:
【中文标题】Svelte.js - 如何使用新道具重新渲染子组件?【英文标题】:Svelte.js - How to rerender child components with new props? 【发布时间】:2020-01-20 12:07:54 【问题描述】:我正在使用 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>
这是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>
当我运行应用程序和 inc/dec pageSelected
时,pageData
属性成功更改 - 如 pre
元素所示。但是,label
和 input
元素与它们在第一页上的完全相同。包裹div
元素的id
和input
元素的id
也没有改变。
当我输入input
并更改页面时,文本保持不变。
我的目标是:当pageSelected
更改时重新渲染FormPage
组件,并让input
和label
根据这些新道具更改它们的值。也应该是,当我更改页面时,已经输入 input
的文本应该更新并为空(因为没有为输入赋予初始值)。
在 React.js 中完成多步骤表单后,我将使用唯一的 key
属性来确保每次 pageSelected
状态更改时我的 FormPage
都会重新呈现。但我不确定如何在 Svelte.js 中做类似的事情。
有什么建议吗?
更新:
阅读 *** 上的 question 后,我了解了如何更改 input
和 label
元素以及 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>
但是,input
里面的文字仍然没有改变。
有没有办法同时更新输入的值?对于我的用例来说,每次道具更改时创建一个全新的元素是理想的,但似乎 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>
这解决了更新输入值的直接问题。
我添加了onMount
、afterUpdate
和onDestroy
函数来查看组件如何随时间变化。
首先调用onDestroy
函数,然后调用onMount
。这些都只触发一次。但是,afterUpdate
会在每次道具更改时触发。这证实了我对组件没有被重新创建的怀疑。
【问题讨论】:
【参考方案1】:有一种更简单的方法可以做到这一点。 pageData
在您的FormPage
中更新,问题是当您使用const name, label = pageData;
时,您基本上是将pageData
中的name
和label
参数的值分配给两个常量name
和label
分别。这两个变量不再绑定到父组件。为了解决这个问题,请直接在FormPage
中使用pageData
,如下所示。
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>
注意:如果您不希望编译器在没有传递
pageData
的情况下给您一个undefined
错误,那么您可以将pageData
初始化为一个空对象,就像
export let pageData = ;
一样。
至于input
问题,最简单的做法是将value
添加到formPages
,然后将bind
将value
添加到pageData.value
,如上图FormPage
所示。
新的formPages
数据
let formPages = [
name: "email",
label: "Email",
value: ""
,
name: "password",
label: "Password",
value: ""
];
这是REPL 上的一个工作示例。
【讨论】:
这是否解决了您的问题?【参考方案2】:#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>
每当currentProp
更改时,Sizing 组件也会更新。
...props
映射到导出的变量
// Sizing.svelte
<script>
export let min
export let max
export let step
</script>
【讨论】:
谢谢你,它完美地解决了我的问题!我只需要这样做: #key changedProp以上是关于Svelte.js - 如何使用新道具重新渲染子组件?的主要内容,如果未能解决你的问题,请参考以下文章