Условная автоподписка в магазине Svelte

Этапы воспроизведения:

Ожидается:

Я думал, что $store может подписаться (с $:) только, если canSubscribe истинно.

Возникает вопрос: Зачем $store подписываться, если canSubscribe неверно?

Я ошибся?


person Fred Hors    schedule 15.04.2020    source источник
comment
Не используйте ссылки с кодом, предоставьте минимальный воспроизводимый пример   -  person Francisco    schedule 16.04.2020


Ответы (3)


Я ошибся?

да. Но не вините себя, ваши ожидания кажутся мне логичными. Но это не так.

Как правило, если где-то в коде вашего компонента есть переменная с префиксом $, тогда это должно быть хранилище, и она будет подписана сразу при создании компонента и отписана, когда компонент будет уничтожен. .

Небольшое исключение из этого правила было введено совсем недавно (с помощью этого PR). Я позволю вам пройти по следу кроличьей норы, если вы хотите знать всю дискуссию. Суть в том, что теперь подписка магазина должна быть или нулевой (то есть null или undefined - см. этот комментарий).

Это означает, что теперь можно при необходимости взломать свой путь к ожидаемому поведению. Мы возвращаемся к этому.

Зачем подписываться на $ store, если canSubscribe ложно?

Потому что на магазины подписываются сразу. Насколько я понимаю, из обсуждения проблем, связанных выше, это для производительности (размер байта) и работоспособности (быстро и заметно сбой, если кто-то пытается подписаться на что-то, что не является магазином). Для меня это имеет смысл.

А теперь вернемся к вопросу, который вы не задавали: как подписаться только тогда / при необходимости? Помещайте хранилище в переменную с автоматической подпиской только тогда, когда это необходимо, и оставляйте его равным нулю в противном случае.

Не делайте:

$: started && $store

Вместо этого сделайте это:

$: proxyStore = started ? store : null
$: console.log($proxyStore)

Полный пример (REPL):

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

    const state1 = { subscribed: 0, unsubscribed: 0 }

    const store1 = writable(42, () => {
        state1.subscribed++
        return () => {
            state1.unsubscribed++
        }
    })

    const state2 = { subscribed: 0, unsubscribed: 0 }

    const store2 = writable(43, () => {
        state2.subscribed++
        return () => {
            state2.unsubscribed++
        }
    })

    let started = false

    $: started && $store1

    $: targetStore = started ? store2 : null

    $: $targetStore
</script>

<pre>
started = {started}
store1 = {$store1} {JSON.stringify(state1)}
store2 = {$targetStore} {JSON.stringify(state2)}
</pre>

<button on:click={() => {started = !started}}>
    {started ? 'Start' : 'Stop'}
</button>
person rixo    schedule 19.04.2020

Svelte просматривает AST во время компиляции, чтобы определить автоматические подписки.

Он устанавливает подписку, даже если код доступа к магазину недоступен.

Например:

import {foo} from './stores'

let condition = false

if (condition) { $foo }

Несмотря на то, что $foo технически недоступен, это не будет известно до времени выполнения.

Альтернатива: вы всегда можете использовать ручную подписку, чтобы обойти эту проблему. Пример:

import {onDestroy} from 'svelte'
import {myStore} from './stores.js'

// subscribe when component is created
const unsubscribe = myStore.subscribe(value => {
  // this is called anytime the value of myStore changes
})

// make sure to unsubscribe when component is unmounted
onDestroy(unsubscribe)
person joshnuss    schedule 17.04.2020
comment
@FredHors Я обновил свой ответ, включив в него пример подписки вручную. Более подробную информацию можно найти здесь: svelte.dev/tutorial/auto-subscriptions - person joshnuss; 17.04.2020

В реактивном операторе $: canSubscribe && $store у вас есть выражение, которое нужно вычислить. Поскольку он является реактивным, Svelte должен определить, когда переоценивать это выражение, и это произойдет в двух случаях: при изменении canSubscribe или изменении $store. Таким образом, он должен будет выполнить подписку на оба этих значения, поэтому вы видите, что сразу получаете подписчика в своем коде.

Обратите внимание, что мы часто делаем canDoSomething && canDoSomething() в JavaScript, но это не на 100% то же самое, что выполнение if (canDoSomething) { canDoSomething()}, просто в большинстве случаев имеет тот же эффект.

person Stephane Vanraes    schedule 16.04.2020
comment
Ok. Но также, если я использую if (canSubscribe) { $store }, он подписывается также, если canSubscribe ложно. Теперь это моя проблема. - person Fred Hors; 16.04.2020
comment
да, реактивность срабатывает всякий раз, когда что-либо в блоке изменяется. Я не уверен, чего вы пытаетесь достичь в первую очередь, мой ответ просто указывает, почему это не работает так, как вы ожидаете. - person Stephane Vanraes; 16.04.2020