Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Piping Data around the app via Routing #6

Closed
Cmdv opened this issue Dec 12, 2015 · 7 comments
Closed

Piping Data around the app via Routing #6

Cmdv opened this issue Dec 12, 2015 · 7 comments

Comments

@Cmdv
Copy link
Owner

Cmdv commented Dec 12, 2015

Right now I'm unable to pipe data around the app in a logical way.

Looking at the bulk of the Router here I've had great help from the Cycle community to get to a point where I'm passing data (Counter results) from my home-index and into my the route component.

Where I'm now struggling is being able to subscribe to the CounterState in another part of the app ie: page1 or navbar

My objective is to increment the counter on home and a counter in navbar updates at the same time. When you move to page1 the counter there would reflect the number in the navbar & home.

Any help towards this goal would be greatly appreciated.

I understand that with the upcoming release of Cycle isolated I would be able to just create a single isolated component and put it in different places in the app, but my intention is to try pipe the data through the app as I think this would be more declarative in the long run. Think of situations when you want to build a new page have some of the data shared from another part of the app but you'd also like to adjust that data into a new form.

Using isolate components to pass data around the app is also a bad idea because if you have multiple isolated components then this could be seen as keeping lots of local states.

if you feel the need for a challenge then please send a PR 😄

@ntilwalli
Copy link

I saw your question on Gitter and thought about it. Maybe I'm interpreting the question wrong and if so, let me know, but I think you need to make the counter state an input observable to your components which need that shared value just like you're doing with the observables that are in sources. You could maybe create a function called constructSharedSources which takes the raw sources as an input and outputs a set of observables that you can then send into the Content and Nav just like the other sources, like so:

function makeIncrementCounter () {
  let count = 0
  return x => count++
}

function constructSharedSources (sources) {
  return {
    count$: sources.DOM.select('selector for the button element').events('click')
      .map(makeIncrementCounter())
  }
}

const main = sources => {
    const sharedSources = constructSharedSources(sources)
    const Content = content(sources, sharedSources);
    const Nav = navbar(sources, sharedSources);

    const view$ = Rx.Observable.just(
        view(
            Nav.DOM,
            Content.DOM
        )
    );

    return {
        DOM: view$,
        // .url$ are inside the nav component
        History: Nav.url$,
    }
};

My impression is, as a rule, you can't "pipe data around" using data-flow/FP approaches or selectively share state between different parts of the app. If you want to share state, you have to find a way to construct it from the inputs and make it available at the top of the data-flow diagram so each component can choose to use it or ignore it.

@Cmdv
Copy link
Owner Author

Cmdv commented Dec 13, 2015

thanks for your answer,

I think you understood what my intentions are correctly.

The only concerns I have with your solution is that you would have state inside makeIncrementCounter and ideally I was looking at passing it along with sources as another Object.

I also thought about some sort of global state here but that wasn't the way to go. I might have misunderstood your point entirely though it's quite hard to view the code on a mobile device haha!

@ntilwalli
Copy link

You understood my point. Considering that discussion you linked, I'm currently operating using the top-down philosophy and I am considering creating a driver for my app which manages global state/single-source-of-truth, but I now see that is a controversial approach. I'll have to do more research myself. :)

@Cmdv
Copy link
Owner Author

Cmdv commented Dec 13, 2015

@ntilwalli yeah I was doing the same. I can tell you that pipping is difficult but I'm sure the more you do it the easier it gets, just a bit of a learning curve hence I'm stuck on this right now 😂

@ntilwalli
Copy link

@Cmdv, it's an interesting topic for sure, top-down vs bottom-up. When using bottom-up, doesn't it mean that your components start having dependencies on other components? So code-ordering becomes important and circular-dependencies become possible right? Like if component X relies on some internal state of Y, and Y relies on the internal state of X what do you do? Is the only alternative in that situation to push those pieces of state to a global state driver?

@nissoh
Copy link

nissoh commented Dec 20, 2015

@Cmdv have you tried rx.BehaviorSubject?
we're gona need to create BehaviorSubject somewhere in the lower level and push sharedState to every component.
our side effect's can occur on home's model or anywhere we call .onNext

// somewhere we'll get sharedState which is subject with initial value and a cached observable.
const sharedState = new Rx.BehaviorSubject(42);

const homeModel = (actions, sharedState) => {
  return Rx.Observable
    .merge(actions.inc$, actions.dec$, sharedState.take(1))
    .scan((x, y) =>  x + y)
    .doAction((val) => sharedState.onNext(val)) // preform side effect with our subject
    ;
};

@Cmdv
Copy link
Owner Author

Cmdv commented Dec 30, 2015

I will close this now as I've worked out how to do it which can now be viewed in master 😄

@Cmdv Cmdv closed this as completed Dec 30, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants