Cycle.js: создание мультиселектного компонента. Объединенный поток состояний компонентов опции не выводит измененные состояния
Пример запуска: https://esnextb.in/?gist=978799bf48a7f914cbbd39df0213d672
Я пытаюсь создать компонент множественного выбора для cycle.js, чтобы узнать, хочу ли я заменить текущий пользовательский интерфейс приложения циклическим приложением.
У меня есть один компонент для опций в мультиселекте. Каждый из параметров получает переданные реквизиты в виде {selected: Bool, label: String, value: Int}
Вот обзор кода:
Компонент Option прослушивает щелчки на себя и переключает "выбранный" логический тип, комбинируя поток логических выражений, сгенерированных путем свертывания логических функций "не" при первоначальном логическом значении, передаваемом параметру в props$ source.
Затем компонент Option выводит поток своего состояния, чтобы компонент Multiselect мог ссылаться на него, чтобы вернуть поток выбранных значений и тому подобное.
Компонент Multiselect получает источник $ props, который содержит поток с параметрами [{selected, label, value}...].
Из этих опций он создает поток массивов компонентов Option.
Затем поток компонентов Option объединяется в поток массивов виртуальных доменных деревьев (по одному для каждой опции).
Поток компонентов Option также сведен в поток массивов состояний (по одному для каждой опции).
Вот где начинается проблема:
Оба из вышеперечисленных только войти один раз, если я вызову.observe (console.log) на них.
Чтобы отобразить множественный выбор, я затем сопоставляю поток массивов виртуальных доменных деревьев с опциями генерации виртуального домашнего дерева мультиселекта.
Я также объединяю этот поток vtree с потоком состояния, чтобы можно было использовать console.log для обоих (поскольку наблюдение только регистрирует их один раз).
Когда я их регистрирую, массивы vtree меняются, но состояния всех опций всегда совпадают с их начальными состояниями.
Это происходит только в том случае, если параметры изолированы. Если они не изолированы, они все реагируют на клики по любому из них, но их состояния регистрируются как измененные.
Что здесь происходит? Почему изолировать материю? Потому что все объединенные потоки не срабатывают каждый раз? Что я делаю совершенно не идиоматично?
http://pastebin.com/RNWvL4nf вот и пастин. Я хотел поместить его в webpackbin, но он не загружал нужные мне пакеты.
import * as most from 'most';
import {run} from '@cycle/most-run';
import {div, input, p, makeDOMDriver, li, ul, span, h2} from '@cycle/dom';
import fp from 'lodash/fp'
import isolate from '@cycle/isolate'
// props : { selected : Bool, label : String, value: Int}
function Option({DOM, props$}) {
const click$ = DOM.select('.option').events('click')
// a stream of toggle functions. one for each click
const toggle$ = click$
.map(() => bool => !bool)
/// stream of toggle functions folded upon the inital value so that clicking the option checks if off and on
const selected$ = props$.map(prop => toggle$.scan((b, f) => f(b), prop.selected)).join()
// a stream of states which has the same structure as props, but toggled 'selected' field according to selected$ stream
const state$ = most.combineArray((props, selected) => ({...props, selected}), [props$, selected$])
// the state mapped to a representation of the option
const vtree$ = state$.map(state => {
return li('.option', {class: {selected: state.selected}}, [
input({attrs: {type: 'checkbox', checked: state.selected}}),
p(state.label),
])
})
// returns the stream of state$ so that multiselect can output a stream of selected values
return {
DOM: vtree$,
state$,
}
}
function Multiselect({DOM, props$}) {
// a stream of arrays of isolated Option components
const options$ = props$.map(
options =>
options.map(it =>
isolate(Option)({DOM, props$: most.of(it)})))
// Option({DOM, props$: most.of(it)}))) // comment above line and uncomment this one. Without isolation the component doesn't work correctly, but the states are updated
// a stream of arrays of virtual tree representations of child Option components
const optionsVtree$ = options$.map(options => fp.map(fp.get('DOM'), options))
.map(arrayOfVtree$ => most.combineArray(Array, arrayOfVtree$))
.join()
// a stream of arrays of states of child Option components
const optionStates$ = options$.map(options => fp.map(fp.get('state$'), options))
.map(arrayOfState$ => most.combineArray(Array, arrayOfState$))
.join()
// .map(fp.filter(fp.get('selected')))
// here the virtual trees of options are combined with the state stream so that I can log the state. I only use the virtual dom trees
const vtree$ = optionsVtree$.combine(Array, optionStates$).map(([vtrees, states]) => {
console.log(states.map(state => state.selected)); // this always prints the initial states
// console.log(vtrees); // but the vtrees do change
return div('.multiselect', [
ul('.options', vtrees)
])
})
const sinks = {
DOM: vtree$,
optionStates$
};
return sinks;
}
run(Multiselect, {
DOM: makeDOMDriver('#app'),
props$: () => most.of([
{value: 0, label: 'Option 1', selected: false},
{value: 1, label: 'Option 2', selected: false},
{value: 2, label: 'Option 3', selected: false},
{value: 3, label: 'Option 4', selected: false},
])
});
РЕДАКТИРОВАТЬ: franciscotln любезно сделал рабочий пример, который показал что-то в моем. https://gist.github.com/franciscotln/e1d9b270ca1051fece868738d854d4e9 Если реквизиты представляют собой простой массив, не заключенный в видимую область, это работает. Возможно, потому что, если это наблюдаемая для массивов, она требует наблюдаемой для массивов компонентов вместо простого массива дочерних компонентов.
Однако два не эквивалентны. В моей исходной версии (где параметры представляют собой поток массивов), реквизиты $ могут быть чем-то иным, чем просто наблюдаемым. Это значение может измениться в течение программы, тогда как со вторым я застрял с исходным массивом.
Есть ли какая-то проблема с потоком массивов изолированных компонентов?
const props = [{value: 0, label: 'Option 1', selected: false},
{value: 1, label: 'Option 2', selected: false},
{value: 2, label: 'Option 3', selected: false},
{value: 3, label: 'Option 4', selected: false}]
// a stream of arrays of isolated Option components
const options = props.map(prop =>
isolate(Option)({
DOM,
props$: most.of(prop)
})
);
1 ответ
@tusharmath В круговороте я узнал, что я делаю не так.
Видимо, все, что мне нужно было сделать, это вызвать.multicast() для потока массивов компонентов option:
const options$ = props$.map(
options =>
options.map(it =>
isolate(Option)({DOM, props$: most.of(it)}))).multicast()
Что-то связанное с горячими и холодными наблюдаемыми. Полагаю, что его обожгли холодные наблюдаемые.
Вот его объяснение.
Я постараюсь объяснить столько, сколько я понял от экспертов здесь. isolate присоединяет параметр области к созданному виртуальному узлу dom. теперь в вашем случае вы создаете виртуальный дом дважды
из-за двух подписок здесь - DOM: https://esnextb.in/?gist=978799bf48a7f914cbbd39df0213d672 состояние $: https://esnextb.in/?gist=978799bf48a7f914cbbd39df0213d672 два разных слушателя, использующие разные события, используют разные способы получения данных область действия и элемент dom имеют другую область видимости.