Привязка ввода Svelte прерывается, когда реактивное значение является ссылочным типом?

(Я новичок в Svelte, поэтому вполне вероятно, что я здесь что-то делаю не так)

ОБНОВЛЕНИЕ: я добавил второй, немного другой REPL, который может лучше продемонстрировать проблему. Попробуйте это: https://svelte.dev/repl/ad7a65894f8440ad9081102946472544?version=3.20.1


Я столкнулся с проблемой при попытке привязать ввод текста к реактивному значению.

Я изо всех сил пытаюсь описать проблему словами, поэтому, надеюсь, уменьшенная демонстрация проблемы в прилагаемом REPL будет иметь больше смысла.

https://svelte.dev/repl/6c8068ed4cc048919f71d87f9d020696 >

Демонстрация содержит два пользовательских <Selector> компонента на странице.


Первому компоненту передаются два строковых значения («один» и «два»):

<Selector valueOne="one" valueTwo="two"/>

При нажатии кнопок рядом с полем ввода для selectedValue устанавливается одно из этих значений.

Это, в свою очередь, вызывает обновление следующего реактивного объявления:

$: value = selectedValue

Поле ввода привязано к этому реактивному значению:

<input type="text" bind:value>

Таким образом, нажатие кнопки «Один» устанавливает для вводимого текста значение «один», а нажатие кнопки «Два» устанавливает для поля ввода значение «два».

Важно отметить, что вы по-прежнему можете вводить что угодно в поле ввода.


Второму компоненту передаются два значения массива:

<Selector valueOne={[1, "one"]} valueTwo={[2, "two"]}/>

Опять же, нажатие кнопок устанавливает selectedValue на одно из этих значений.

Однако на этот раз реактивное объявление зависит от элемента массива:

$: value = selectedValue[1]

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

Итак, вопрос - почему <input bind:value> ведет себя по-разному для этих двух:

$: value = aString

vs

$: value = anArray[x]

person Scott    schedule 24.03.2020    source источник


Ответы (2)


Кажется, что это проблема только при использовании двусторонних привязок.

Переключившись на односторонний и on: обработчик ввода, проблема исчезнет:

т.е. вместо этого:

<input type="text" bind:value={valX}/>

использовать этот:

<input type="text" value={valX} on:input={e => valX = e.target.value}/>
person Scott    schedule 30.03.2020

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

Также привязка к реактивному объявлению означает, что вы фактически никогда не меняете переменные с вводом (который вы можете увидеть в своем результате JSON в первом селекторе, когда вы вводите ввод, значение не обновляется только при нажатии кнопки).

Почему бы не потерять реактивное объявление и не привязаться непосредственно к нужной переменной. Затем используйте блок {#if}, чтобы переключаться между той версией ввода, которую вы показываете, в зависимости от правдивости index?

<script>
    export let valueOne;
    export let valueTwo;
    export let index;

    let selectedValue = index? [] : '';
    let selectValue = (val) => selectedValue = val;
</script>
{#if index}
    <input type="text" bind:value={selectedValue[index]} placeholder="Type anything...">
{:else}
    <input type="text" bind:value={selectedValue} placeholder="Type anything...">
{/if}
<button on:click={() => selectValue(valueOne)}>One</button>
<button on:click={() => selectValue(valueTwo)}>Two</button>
<p>
    <strong>Selected value:</strong> {JSON.stringify(selectedValue)}
</p>

Связывая непосредственно с selectedValue или его индексом, вы получаете дополнительное преимущество, заключающееся в изменении значения при вводе. Вот рабочий пример в REPL

person JHeth    schedule 25.03.2020
comment
Я не совсем понимаю ... в примере, где значения являются строками, если я добавлю тот же оператор $: value, console.log(value), связанное значение определенно обновится при вводе? Не работает только версия массива. Причина, по которой я не привязываюсь напрямую к selectedValue, заключается в том, что REPL - это надуманный пример, демонстрирующий проблему. Реальность такова, что я создаю компонент ComboBox (он же typeahead, autocomplete ..), где предложения (массив объектов) отображаются как типы пользователей. Когда выбор сделан, я устанавливаю связанное значение для свойства выбранного объекта. - person Scott; 25.03.2020
comment
В вашем надуманном примере реактивный оператор не должен быть реактивным (индекс устанавливается при монтировании) и вызывает проблему. Он перезаписывает value, что по какой-то причине нормально, когда данные представляют собой строку, а не массив. Вы можете добавить setTimeout, чтобы увидеть его перезапись с задержкой $: setTimeout(()=> value = index ? selectedValue[index] : selectedValue, 200). Вы можете установить привязку в своей функции, например this, если хотите, чтобы это было способ привязки. Не уверен в вашем поле со списком, но не вижу его. - person JHeth; 25.03.2020
comment
что по какой-то причине нормально, когда данные представляют собой строку, а не массив - да, это, в конечном счете, вопрос, на который я надеюсь, что кто-то может помочь мне ответить. (Конечно, есть много обходных путей, но я действительно пытаюсь понять основную причину, по которой массивы и строки ведут себя по-разному. Я ценю ваши предложенные обходные пути, но вы на самом деле не ответили на вопрос.) - person Scott; 26.03.2020
comment
Кажется, он устанавливает значение вместо привязки к нему (на самом деле ни один из них не привязан к selectedValue только к значению, которое ссылается на selectedValue), но я все еще не понимаю, зачем вам когда-либо нужно использовать его в этом контексте. В вашем примере нет необходимости в реактивном объявлении значения, к которому вы привязываетесь, поскольку для компонента устанавливается индекс, даже если он изменялся после монтирования, переназначение могло быть обработано в функции, вызывающей изменение. Я не знаю, есть ли простой ответ на ваш вопрос, но похоже, что вы изначально неправильно используете реактивные объявления. - person JHeth; 26.03.2020