Назначение хранилища Svelte вызывает defaultwritable (). Set, а затем custom .set?

Соответствующий REPL

У меня есть простое записываемое хранилище с настраиваемым методом set:

import { writable } from 'svelte/store';
function createState() {
    const {subscribe, set, update} = writable({a: 0, b: 0});
    return {
        subscribe,
        set: (newState) => {
            console.log(newState);
            // set(newState); // I would expect `state` to be unchanged without this
        }
    };
};

export const state = createState();

Когда я вызываю state.set(<some new value>), новое значение записывается в консоль, и значение state фактически не меняется. Это то, что я ожидал.
Однако, если я назначу $state = <some new value>, значение state изменится, и затем set записывает его в консоль. Почему (и как) это происходит, и есть ли способ обойти это без повторной реализации writable?

Спасибо!


person John LaRocque    schedule 22.07.2020    source источник


Ответы (3)


Это ожидаемое поведение! Забудьте на мгновение о магазинах, и - поскольку state.set в вашем примере - noop - удалите эту строку:

<script>
    // import { state } from "./stores.js";

    let pojo = { a: 0, b: 0 };
    pojo.a = 1;
    pojo = {d: 0};

    //state.set({c: 0}); // no effect, as expected
</script>

<h1>state: {JSON.stringify(pojo)}</h1> 

Результат...

state: {"d":0}

... именно то, что вы ожидали.

Когда мы заменяем pojo на $state, мы по-прежнему назначаем (или изменяем) локальную переменную с теми же характеристиками реактивности. Единственная разница в том, что мы также вызываем state.set. Обычно это приводит к изменению значения хранилища, которое отразится на его подписчиках, но поскольку ваше настраиваемое хранилище этого не делает, у нас остается значение после последнего касания $state оператором присваивания.

person Rich Harris    schedule 23.07.2020
comment
Большое спасибо за объяснение! Я принял этот ответ, потому что он объясняет, почему такое поведение. Если кто-то сталкивается с этим вопросом и хочет узнать больше о том, что происходит, Томас и Андреас также предоставили отличное понимание. (Между прочим, я изначально задал этот вопрос, потому что хотел обновить свой магазин, чтобы иметь побочные эффекты вместо / перед назначением. Похоже, что это не было бы Svelteish, поэтому я реструктурировал свое приложение так, чтобы логика была перемещена в другое место.) - person John LaRocque; 23.07.2020
comment
Объяснение оказалось намного проще, чем я ожидал, но все же очень логичным. Спасибо, Рич! - person Thomas Hennes; 24.07.2020

В JS-выходе вашего REPL вы можете увидеть следующее:

function instance($$self, $$props, $$invalidate) {
    let $state;
    component_subscribe($$self, state, $$value => $$invalidate(0, $state = $$value));
    set_store_value(state, $state.a = 1, $state); // changes the value of state?!
    set_store_value(state, $state = { d: 0 }); // ''
    state.set({ c: 0 }); // no effect, as expected
    return [$state];
}

При использовании сокращенного реактивного присваивания $<store> компилируется в set_store_value() вызовы, которые представляют собой компактный внутренний метод, определенный таким образом:

export function set_store_value(store, ret, value = ret) {
    store.set(value);
    return ret;
}

Таким образом, назначенное значение фактически передается функции set вашего магазина, как и следовало ожидать.

Однако вы можете видеть в выходных данных JS выше, что в конечном итоге возвращается локальная переменная с именем $state (знак $ там не реактивный модификатор, просто часть имени). Во время этих вызовов set_store_value() вы можете видеть, что этой локальной переменной присваивается то же значение, которое передается методу set вашего магазина (фактически, локальной переменной присваивается это значение, а затем она сама передается методу set_store_value()):

set_store_value(state, $state.a = 1, $state); // changes the value of state?!
set_store_value(state, $state = { d: 0 }); // ''

Я ожидаю, что такое поведение будет своего рода оптимистичным прогнозом / разрешением.

Возможно, в контракте магазина Svelte подразумевается, что значение, переданное set методу магазина , должно на самом деле измените хранилище соответствующим образом, и в каком случае подход оптимистичного разрешения всегда будет давать согласованные результаты?

Надеюсь, Рич Харрис (или другой участник Svelte) увидит ваш вопрос и предоставит более точный ответ.

person Thomas Hennes    schedule 22.07.2020

Расширяя большой анализ Томаса Хеннеса:

set_store_value(state, $state.a = 1, $state);

Этот внутренний вызов обновляет модель внутреннего хранилища ($state.a = 1) без использования метода set или update. Для меня это немного неожиданно и, вероятно, изящная ошибка. По крайней мере, это то, что нам нужно иметь в виду, когда мы проектируем сложные специализированные магазины и хотим использовать их сокращения.

person Andreas Dolk    schedule 22.07.2020