Svelte中父组件更新子组件响应式变量失效问题
问题根源
你的问题核心在于子组件TableRow内部的isCollapsed状态和父组件App里的isCollapsed是完全独立的两个变量。父组件的toggleCollapsible函数修改的是自己的isCollapsed,根本没同步到子组件,所以子组件的图标(加号/减号)不会更新。另外,父组件直接操作DOM切换类的方式也不符合Svelte的响应式理念,应该通过状态驱动视图。
解决方案:将折叠状态提升到父组件管理(推荐)
把每一行的折叠状态统一放在父组件中维护,通过props传递给子组件,点击时子组件通知父组件切换对应行的状态,父组件更新后自动同步到子组件。
步骤1:修改父组件App.svelte
- 初始化一个对象存储每一行的折叠状态,默认所有行折叠:
// 替换原来的let isCollapsed; let collapsedStates = {}; // 初始化现有行的默认折叠状态 $: if (table) { table.forEach(t => { if (!(t.id in collapsedStates)) { collapsedStates[t.id] = true; } }); } - 重写
toggleCollapsible函数,通过更新状态代替手动操作DOM:function toggleCollapsible(rowId) { collapsedStates[rowId] = !collapsedStates[rowId]; } - 渲染
TableRow时传递对应行的折叠状态和回调:<TableRow id={t.id} labels={labels} toggleCollapsible={() => toggleCollapsible(t.id)} rowData={t} isCollapsed={collapsedStates[t.id]} /> - 折叠行的显示通过状态控制:
注:<tr id="row_form_{t.id}" class:show={!collapsedStates[t.id]} aria-expanded={!collapsedStates[t.id]} > <td colspan="{colspan}"> <FormRow onSubmit={onSubmit}/> </td> </tr>show类的显示逻辑和isCollapsed相反——isCollapsed为true时折叠行隐藏,所以用!collapsedStates[t.id]控制。
步骤2:修改子组件TableRow.svelte
- 把内部的
isCollapsed改为接收父组件的props,删除无效的内部状态声明:export let rowData = {}; export let labels = {}; export let id = -1; export let toggleCollapsible = function(){}; export let isCollapsed; // 接收父组件传递的状态 - 点击事件直接调用传递的回调,无需传递事件对象:
这里可以去掉<span role="button" on:click={toggleCollapsible}> {labels.realised} [{#if isCollapsed}<i class="fa fa-plus"></i>{:else}<i class="fa fa-minus"></i>{/if}] </span>data-row属性,因为已经通过闭包把行ID传递给了父组件回调。
另一种方案:子组件自管理状态并通知父组件
如果更倾向子组件控制自身状态,可通过事件通知父组件更新折叠行显示:
子组件TableRow.svelte修改:
import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); let isCollapsed = true; function handleClick() { isCollapsed = !isCollapsed; dispatch('toggle', { id, isCollapsed }); }
<span role="button" on:click={handleClick}> {labels.realised} [{#if isCollapsed}<i class="fa fa-plus"></i>{:else}<i class="fa fa-minus"></i>{/if}] </span>
父组件App.svelte修改:
<TableRow id={t.id} labels={labels} rowData={t} on:toggle={(e) => { const { id, isCollapsed } = e.detail; const tr = document.getElementById(`row_form_${id}`); tr.classList.toggle('show', !isCollapsed); tr.setAttribute('aria-expanded', !isCollapsed); }} />
这种方式可行,但不如第一种状态驱动的方式符合Svelte最佳实践,因为第一种完全避免了手动操作DOM,所有视图变化由状态控制。
内容的提问来源于stack exchange,提问作者Hmerman6006
相关产品推荐
相关产品推荐

