Почему массив доступных для записи корректно не обновляет значения внутри компонента Svelte? - PullRequest
0 голосов
/ 07 марта 2020

У меня есть родительский шаблон (App.svelte), который создает массив доступных для записи и передает его компоненту (RedOrBlueOrPurple.svelte). Компонент перебирает элементы. Я хочу передать массив доступных для записи (вместо создания единственной доступной для записи, которая содержит весь массив объектов), потому что я хочу добавить обработчики subscribe для каждого элемента индивидуально, когда вещи изменяются внутри элемента. В противном случае мой обработчик подписки должен был бы выяснить, какой элемент был изменен, что выглядит как дополнительная работа.

В этом коде ниже первая кнопка действительно меняет фиолетовый атрибут, потому что за этим вызовом update следует refresh, который сбрасывает родительские значения. Я хотел бы избежать этого вызова, если это возможно, и просто использовать доступный для записи в качестве интерфейса, который понимают и могут использовать родитель и ребенок.

Кроме того, мне не нравится, что шаблон должен использовать map на каждом, чтобы вытащить предмет, получите значение магазина (используя внутренний вызов svelte get_store_value), но я не знаю, как бы я использовал {#each items as $items...} в противном случае (я не могу понять, как использовать $item Вот). Есть ли более элегантный способ получения значения?

Кроме того, и это кажется очень прискорбным, тот факт, что консоль не отображает ошибки, когда я вызываю refre sh и задаю переменную const, выглядит так он нарушает правила JS и все еще компилируется. Я знаю, что Svelte комментирует этот призыв под капотом, чтобы отреагировать на изменение, но это кажется неправильным.

Это на этом REPL здесь: https://svelte.dev/repl/84fbae4358494844a79a7f119b084f01?version=3.19.2

enter image description here

<script>
    import { writable } from 'svelte/store';
    import RedOrBlueOrPurple from './RedOrBlueOrPurple.svelte';

    const items = [];
    items.push( writable( { blue: true }));
    items.push( writable( { blue: false, red: true }));
    items.push( writable( { blue: true, red: true }));

const refresh = () => {
    items = items;
};

</script>

<RedOrBlueOrPurple {refresh} {items}/>

<script>
    import { get_store_value } from 'svelte/internal';
    export let items;
    export let refresh;

    const purple = (item, callRefresh) => {
        item.update( i => {
            i.purple = true;
            return i;
        });
        // We only call this for the first button, and if we don't, 
        // the items aren't updated, even though we do call update on
        // the store correctly. 
        if (callRefresh) {
            refresh();
        }
    };
</script>

{#each items.map( item => ({ item, value: get_store_value(item) })) as { item, value}, index }

<h1>
    Item : { index }
</h1>

<div>
RED: { value.red ? "Red" : "Not red"}
</div>
<div>
BLUE: { value.blue ? "Blue" : "Not blue"}
</div>

<div>
PURPLE: { value.purple ? "Purple" : "Not purple"}
</div>

<button on:click={ () => { purple(item, index === 0) } }>Change to purple</button>

{/each}

Ответы [ 2 ]

2 голосов
/ 07 марта 2020

Обновление экрана запускается, в вашем случае, только если svelte обнаруживает присвоение items, которое вы используете в #each. Просто обновить любой из элементов массива недостаточно. Вы можете просто решить это так:

const purple = (item, callRefresh) => {
    item.update( i => {
        i.purple = true;
        return i;
    });
    items = items;  // instead of using the callback, simple reassign items here
};
1 голос
/ 11 марта 2020

С этим можно справиться, используя производное хранилище:

$: values = derived(items, x => x)

Таким образом, ваш код станет:

App.svelte

<script>
    import { writable } from 'svelte/store';
    import RedOrBlueOrPurple from './RedOrBlueOrPurple.svelte';

    let items = [];
    items.push( writable( { blue: true }));
    items.push( writable( { blue: false, red: true }));
    items.push( writable( { blue: true, red: true }));

    const more = () => {
        items = [writable({}), ...items]
    }
</script>

<!-- another button to ensure new items are processed correctly -->
<button on:click={more}>More</button>

<RedOrBlueOrPurple {items}/>

RedOrBlueOrPurple.svelte

<code><script>
    import { derived } from 'svelte/store'

    export let items = [];

    $: values = derived(items, x => x)

    const purple = (item, callRefresh) => {
        item.update(i => ({ ...i, purple: true }))
    };
</script>

{#each $values as value, index}
    <h1>Item: {index}</h1>
    <pre>{JSON.stringify(value)}
{фиолетовый (элементы [индекс])}}}> Изменить на фиолетовый {/ каждый}

Обновлено REPL

Примечание

Не:

import { get_store_value } from 'svelte/internal';

Существует API-эквивалент publi c (см. документы ):

import { get } from 'svelte/store'
...