【发布时间】: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 中做类似的事情。
有什么建议吗?
更新:
阅读 StackOverflow 上的 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 会在每次道具更改时触发。这证实了我对组件没有被重新创建的怀疑。
【问题讨论】:
标签: javascript forms state svelte