Как обеспечить переходы только локально, когда Svelte повторно использует родительский элемент dom
В Svelte у меня есть компонент, который используется для отображения элементов в двух разных списках. Когда эти элементы перемещаются из одного списка в другой, они используют переход для анимации входа или выхода.
Однако у меня также есть способ фильтровать то, что отображается на экране. Отображение нового набора элементов будет использовать тот же компонент, но с другими данными. В этом случае я не хочу, чтобы анимация перехода происходила. Я предположил, что добавивlocal
модификатор поможет, но похоже, что Svelte не удаляет родительский элемент в список, а вместо этого повторно использует его и добавляет новые данные в существующий элемент DOM списка.
Я попытался воспроизвести то, что вижу в приведенном ниже примере кода.
Разыскиваемое поведение:
- Нажатие на TODO переключит TODO из одного списка в другой.
- Нажатие "Переключить категории" переключит перечисленные TODO без анимации
<li>
s из TODO, которые добавляются или удаляются.
Фактическое поведение:
- Происходит как положено.
- Включенные задачи выполняются вместе с анимацией.
Как я могу изменить свой пример, чтобы получить желаемый эффект?
App.svelte:
<script>
import Todos from './Todos.svelte';
let todos = [
{ id: 1, category: 'personal', name: 'Walk dog', done: false },
{ id: 2, category: 'personal', name: 'Take out trash', done: false },
{ id: 3, category: 'work', name: 'Make login page functional', done: false },
{ id: 4, category: 'work', name: 'Make login page elegant', done: false }
];
let currentCategory = 'personal';
const toggleCategory = () => {
currentCategory = currentCategory == 'personal' ? 'work' : 'personal';
}
const toggleTodo = id => {
todos = todos.map(todo => {
if (todo.id === id) {
return { ...todo, done: !todo.done }
}
return todo;
});
}
$: categoryTodos = todos.filter(x => x.category == currentCategory);
</script>
<button on:click={toggleCategory}>Switch Categories</button>
<Todos todos={categoryTodos} {toggleTodo}>
</Todos>
Todos.svelte:
<script>
import { slide } from 'svelte/transition';
export let todos;
export let toggleTodo;
$: complete = todos.filter(t => t.done);
$: incomplete = todos.filter(t => !t.done);
</script>
<h1>Incomplete</h1>
<ul>
{#each incomplete as {id, name} (id)}
<li transition:slide|local on:click={() => toggleTodo(id)}>{name}</li>
{/each}
</ul>
<h1>Complete</h1>
<ul>
{#each complete as {id, name} (id)}
<li transition:slide|local on:click={() => toggleTodo(id)}>{name}</li>
{/each}
</ul>
1 ответ
В React вы бы использовали key
prop, чтобы средство визуализации воссоздало элемент, который можно было использовать повторно (тот же тег и т. д.).
// React
<Todos items={items} key={currentCategory} />
Но Svelte не поддерживает key
, Является ли? Ну, отчасти. У Svelte есть аналогичная функция, но только в{#each ...}
блоки.
Синтаксис следующий (документы - этот точный синтаксис не упоминается в документах, но я думаю, он просто забыт):
{#each expression as name (key)}...{/each}
Как и в React, компонент будет воссоздан при изменении значения ключа (в противном случае используется повторно).
И таааак...
<script>
export let allItems
export let currentCategory
$: items = allItems.filter(x => x.category === currentCategory)
</script>
{#each [items] as todos (currentCategory)}
<Todos {todos} />
{/each}
Ху ху. Правильно?
С помощью currentCategory
в качестве ключа создаст новый <Todos />
компонент каждый раз, когда категория изменяется, что, вероятно, вы хотите в вашем случае.
Как и в React, значение ключа должно выбираться с умом, чтобы воссоздавать его каждый раз, когда это необходимо, но не более (иначе в вашем случае это убьет желаемый переход между элементами).
Значение ключа не ограничивается текущим оцениваемым элементом в каждом цикле. Он может поступать из любой точки Svelte, поэтому вы можете проявить творческий подход. Это может быть даже встроенный объект{}
который бы воссоздавать... Ну в принципе все время!
редактировать
Вы можете превратить взлом в отдельный компонент для более чистого синтаксиса в потребителях:
<!-- Keyed.svelte -->
<script>
export let key
// we just need a 1-length array
const items = [0]
</script>
{#each items as x (key)}
<slot />
{/each}
Используйте так:
<script>
import Keyed from './Keyed.svelte'
export let items
export let category
</script>
<Keyed key={category}>
<Todos {items} />
</Keyed>
См. Пример в REPL Svelte.