Giter VIP home page Giter VIP logo

Comments (18)

staltz avatar staltz commented on June 8, 2024

One alternative is to manually use isolateSource/isolateSink to have more control.

from cycle-onionify.

Hypnosphi avatar Hypnosphi commented on June 8, 2024

Well, in more complex example there would be more drivers, so it would mean reimplementing isolate

from cycle-onionify.

Widdershin avatar Widdershin commented on June 8, 2024

Maybe a higher order component to allow communication between the slider and the inputs? Only problem I see is that the state would be a memory stream, not ideal for a circular dependency.

from cycle-onionify.

Hypnosphi avatar Hypnosphi commented on June 8, 2024

Maybe onionify could use some prefix in isolation key? like isolate(Component, 'onion-foo'). It would be more writing, but also would allow the cases where the component tree doesn't fully replicate the state tree

from cycle-onionify.

staltz avatar staltz commented on June 8, 2024

Maybe a higher order component to allow communication between the slider and the inputs? Only problem I see is that the state would be a memory stream, not ideal for a circular dependency.

With onionify there wouldn't be a need to make the circular dependency. Or are you thinking that would be a product of the higher order component?

Maybe onionify could use some prefix in isolation key? like isolate(Component, 'onion-foo'). It would be more writing, but also would allow the cases where the component tree doesn't fully replicate the state tree

I'd prefer not to have any magic going on. Here's it written down all manually:

function main(sources) {
  const rangePickerSources = {
    DOM: sources.DOM.isolateSource(sources.DOM, 'RangePicker'),
    HTTP: sources.HTTP.isolateSource(sources.HTTP, 'RangePicker'),
    onion: sources.onion.isolateSource(sources.onion, 'range'),
  };
  const rangePickerSinks = RangePicker(rangePickerSources);
  const isolatedRangePickerSinks = {
    DOM: sources.DOM.isolateSource(rangePickerSinks, 'RangePicker'),
    HTTP: sources.HTTP.isolateSource(rangePickerSinks, 'RangePicker'),
    onion: sources.onion.isolateSource(rangePickerSinks, 'range'),
  };

  const fromSources= {
    DOM: sources.DOM.isolateSource(sources.DOM, 'From'),
    HTTP: sources.HTTP.isolateSource(sources.HTTP, 'From'),
    onion: sources.onion.isolateSource(sources.onion, 'range'),
  };
  const fromSinks = From(fromSources);
  const isolatedFromSinks = {
    DOM: sources.DOM.isolateSource(fromSinks, 'From'),
    HTTP: sources.HTTP.isolateSource(fromSinks, 'From'),
    onion: sources.onion.isolateSource(fromSinks, 'range'),
  };

  const toSources= {
    DOM: sources.DOM.isolateSource(sources.DOM, 'To'),
    HTTP: sources.HTTP.isolateSource(sources.HTTP, 'To'),
    onion: sources.onion.isolateSource(sources.onion, 'range'),
  };
  const toSinks = To(toSources);
  const isolatedToSinks = {
    DOM: sources.DOM.isolateSource(toSinks, 'To'),
    HTTP: sources.HTTP.isolateSource(toSinks, 'To'),
    onion: sources.onion.isolateSource(toSinks, 'range'),
  };

  // ... combine child sinks together

  return sinks;
}

And we could also enhance isolate to support a function that runs for each sink/source type to choose what scope to use:

function main(sources) {
  const chooseRangePickerScope = key =>
    key === 'onion' ? 'range' : 'RangePicker';
  const rangePickerSinks = isolate(RangePicker, chooseRangePickerScope)(sources);

  const chooseFromScope = key =>
    key === 'onion' ? 'range' : 'From';
  const fromSinks = isolate(From, chooseFromScope)(sources);

  const chooseToScope = key =>
    key === 'onion' ? 'range' : 'To';
  const toSinks = isolate(To, chooseFromScope)(sources);

  // ... combine child sinks together

  return sinks;
}

We need to consider it a bit more carefully. I vaguely remember @axefrog or someone else talking about "isolate only this one" type of feature.

from cycle-onionify.

staltz avatar staltz commented on June 8, 2024

Oh, actually I just found a better idea: pass state$, not the onion StateSource down to the children, and manually isolate their sinks (the reducer from each child):

function main(sources) {
  const childrenSources = {
    ...sources,
    state: sources.onion.isolateSource(sources.onion, 'range').state$,
  };

  const rangePickerSinks = isolate(RangePicker)(childrenSources);
  const fromSinks = isolate(From)(childrenSources);
  const toSinks = isolate(To)(childrenSources);

  const childrenReducer$ = xs.merge(
    sources.onion.isolateSink(rangePickerSinks.onion, 'range'),
    sources.onion.isolateSink(fromSinks.onion, 'range'),
    sources.onion.isolateSink(toSinks.onion, 'range')
  );

  // ... combine child sinks together

  return sinks;
}

from cycle-onionify.

Hypnosphi avatar Hypnosphi commented on June 8, 2024

pass state$, not the onion StateSource

sounds ok

  const fromSinks = isolate(From)(childrenSources);

  const childrenReducer$ = xs.merge(
    sources.onion.isolateSink(fromSinks.onion, 'range')
  );

We have a double isolation here. Are you sure it will work as expected? I think, the children should just return reducers sink here, which will be merged into parent's onion sink

from cycle-onionify.

staltz avatar staltz commented on June 8, 2024

Yes it will work, because fromSinks.onion is not isolated. sources.onion.isolateSink function is not available for the child component, because we didn't give sources.onion to the child.

UPDATE:
Ok, actually, we did pass sources.onion to the children (through the object spread). In that case we could have each child return reducers under the key reducer and not onion. Or some variation of this idea.

from cycle-onionify.

mspoulsen avatar mspoulsen commented on June 8, 2024

I tried creating a webpackbin but it wouldn't let me. I created a very simple example (I like those) with two components:

  1. Incrementer
  2. Double

The components are siblings and I would like to pass output from the Incrementer into Double. Can someone explain best practices for that when using onionify?

Since webpackbin is not working I will pase code here:

import { run }                              from '@cycle/xstream-run';
import { button, div,
            makeDOMDriver }                 from '@cycle/dom';
import xs                                   from 'xstream';
import isolate                              from '@cycle/isolate';
import onionify                             from 'cycle-onionify';


function Double(sources) {

    // This component should:
    // 1. take data from Increment,
    // 2. double it and store it as state ({ value })
    // 3. displays result

    // What is recommend way to do that with onionify?

    const state$        = sources.onion.state$;
    const fnInit$       = xs.of(() => ({ value : 0 }));
    const reducer$      = xs.merge(fnInit$);
    const vdom$         = state$
                            .map(state => div([
                                div('Double : ' + state.value.toString()),
                            ]));

    return {
        DOM     : vdom$,
        onion   : reducer$
    }


}

function Incrementer(sources) {

    const state$        = sources.onion.state$;
    const action$       = sources.DOM
                            .select('.inc')
                            .events('click');

    const fnInit$       = xs.of(() => ({ count : 0 }));
    const fnInc$        = action$.mapTo(state => ({ count : state.count + 1 }));
    const reducer$      = xs.merge(fnInit$, fnInc$);
    const vdom$         = state$.map(state => div([
                                    div(state.count.toString()),
                                    button('.inc', '+')
                            ]));

    return {
        DOM     : vdom$,
        onion   : reducer$
    }

}



function main(sources) {

    const state$            = sources.onion.state$;

    const fnInit$           = xs.of(() => ({ }));
    const incrementer       = isolate(Incrementer, 'inc')(sources);
    const double            = isolate(Double, 'double')(sources);

    const reducer$          = xs.merge(fnInit$, incrementer.onion, double.onion);

    const vdom$             = xs.combine(state$, incrementer.DOM, double.DOM)
                                .map(([ state, incrementer, double ]) => div([
                                    incrementer,
                                    double,
                                    div(JSON.stringify(state)),

                                ]));

    return {
        DOM: vdom$,
        onion: reducer$,
    };
}


const wrappedMain   = onionify(main);

run(wrappedMain, {
    DOM: makeDOMDriver('#app')
});

from cycle-onionify.

staltz avatar staltz commented on June 8, 2024

@mspoulsen I think Double doesn't need any reducer because it's not modifying state. Then you can just pass count$ instead of the onion state source.

function main(sources) {

    const state$            = sources.onion.state$;

    const fnInit$           = xs.of(() => ({ }));
    const incrementer       = isolate(Incrementer, 'inc')(sources);
    const count$            = state$.map(state => state.count);
    const double            = isolate(Double, 'double')({DOM: sources.DOM, count$}); // this

    const reducer$          = xs.merge(fnInit$, incrementer.onion);

    const vdom$             = xs.combine(state$, incrementer.DOM, double.DOM)
                                .map(([ state, incrementer, double ]) => div([
                                    incrementer,
                                    double,
                                    div(JSON.stringify(state)),

                                ]));

    return {
        DOM: vdom$,
        onion: reducer$,
    };
}

And Double

function Double(sources) {
    const vdom$         = sources.count$
                            .map(count => count * 2)
                            .map(count => div([
                                div('Double : ' + count.toString()),
                            ]));

    return {
        DOM     : vdom$,
    }
}

from cycle-onionify.

mspoulsen avatar mspoulsen commented on June 8, 2024

Ok, so I map state and pass that into Double. That was the pattern I was unsure about. In my real app Double will have reducers, but I will try this first and see where that gets me. Thanks a lot! :)

from cycle-onionify.

Hypnosphi avatar Hypnosphi commented on June 8, 2024

@staltz i guess it should be const incrementer = isolate(Incrementer, 'count')(sources);

from cycle-onionify.

mspoulsen avatar mspoulsen commented on June 8, 2024

This is a more accurate example. I would like to pass into Double the value from Incrementer, then I want to apply some logic and store some derived data as state inside Double and expose it for other components to read from.

Here I run into a loop, but I am not sure what I am doing wrong:
https://gist.github.com/mspoulsen/c5bea0f1337e3058e82f91815b1cc123

from cycle-onionify.

Hypnosphi avatar Hypnosphi commented on June 8, 2024

The loop starts here: https://gist.github.com/mspoulsen/c5bea0f1337e3058e82f91815b1cc123#file-onionify-js-L23

Here you change the state when the state changes (and remove state.Incrementer, but that's another issue)

Just do double$ = count$.map(c => c * 2) and use it. If you want to share it, declare it on the closest common ancestor component

from cycle-onionify.

mspoulsen avatar mspoulsen commented on June 8, 2024

You mean something like this: https://esnextb.in/?gist=7bc7670a8212bf1ee939f33a1122b4b4 ?

It works fine. I am not very familiar with single source of truth trees. I guess we don't store derived data in them anyway? If this is the way to go then everything is dandy :)

Update: Hmm...some issue with link. Working now.

from cycle-onionify.

mspoulsen avatar mspoulsen commented on June 8, 2024

Link updated. Was linking to previous example before (just for clarity): https://esnextb.in/?gist=7bc7670a8212bf1ee939f33a1122b4b4

from cycle-onionify.

Hypnosphi avatar Hypnosphi commented on June 8, 2024

I guess we don't store derived data in them anyway

Yes, it's a good idea to keep derived data away from state tree. If the deriving process is expensive, you may consider memoization

from cycle-onionify.

staltz avatar staltz commented on June 8, 2024

I believe issue #11 solves this very well now

from cycle-onionify.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.