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

Rx pushing data through React-Router #1230

Closed
Cmdv opened this issue May 25, 2015 · 8 comments
Closed

Rx pushing data through React-Router #1230

Cmdv opened this issue May 25, 2015 · 8 comments

Comments

@Cmdv
Copy link

Cmdv commented May 25, 2015

I have a react App with the data flow being driven by RxJS -> here

Short of it is I subscribe to my data store which I then pump through via the props:

Model.subject.subscribe((appState) => {
  React.render(
    <App {...appState}/>,
    document.getElementById('app')
  );
});

Now is there a way I can merge the above subscription into the Router.run..?

Router.run(routes, function (Handler) {
  React.render(
    <Handler />,
    document.getElementById('app')
  );
});

I've tried the bellow the router still works but the appState isn't been passed through!!

 Model.subject.subscribe((appState) => {
  Router.run(routes, function (Handler) {
    React.render(
      <Handler {...appState}/>,
      document.getElementById('app')
    );
  });
});

Any pointers would be superb thanks :)

@rarous
Copy link

rarous commented May 25, 2015

You have a typo in {...appstate}. It should be {...appState}

@Cmdv
Copy link
Author

Cmdv commented May 25, 2015

Thanks edited, looking into how flux interacts with React-router maybe I can get some clues there.

@BerkeleyTrue
Copy link

What you want is a for an observer to be notified when both Router.run and Model.subject publish new values.

This calls for... combineLatest!

First, make Router.run into an observable

const RouterObs = Rx.Observable.create(observer => {
    Router.run(routes, location, (Handler, state) => {
      observer.onNext({ Handler, state });
    });
  });
};

Now sprinkle in a dash of combineLatest!

const subscription = Rx.Observable.combineLatest(
  Model.subject,
  RouterObs,
  function((appState, { Handler, State }) => ({
    appState,
    Handler,
    State
  }))
  .subscribe(({ appState, Handler, State }) => {
    React.render(
      <Handler {...appState}/>,
      document.getElementById('app')
    );
  });
});

Bam! Rx-ified Router Soup!

Hope that answers your question.

P.S. Adds some spice by introducing flatMap and Rx-ifying render ala Thundercats.js (disclaimer: I am the author of Thundercats)

const ObservableRender = new Rx.AnonymousObservable(function (observer) {
    let instance = null;
    instance = React.render(Comp, DOMContainer, (err) => {
      if (err) { return observer.onError(err); }
      if (instance) { observer.onNext(instance); }
    });
    observer.onNext(instance);
  });
});

const subscription = Rx.Observable.combineLatest(
  Model.subject,
  RouterObs,
  function((appState, { Handler, State }) => ({
    appState,
    Handler,
    State
  }))
  .flatMap(({ appState, Handler, State }) => {
    return ObservableRender(
      <Handler {...appState}/>,
      document.getElementById('app')
    );
  })
  .subscribe(reactRoot => {
    console.log('booya! React rendered!');
  });
});

@Cmdv
Copy link
Author

Cmdv commented May 26, 2015

@BerkeleyTrue Thanks a lot for the response. I'd seen Thundercats being mentioned on twiter but had forgotten the name just knew it had a cool one 😃

Combine latests comes and saves the day, I'm fairly new to RX but logically it just makes so much sense.

There was one small typo with the code but can't seem to fix it, around this section:

const subscription = Rx.Observable.combineLatest(
  Model.subject,
  RouterObs,
  function((appState, { Handler, State }) => ({
   appState,
   Handler,
   State
}))

I assume we are trying to give back an object with appState, Handler, State so I tried:

const subscription = Rx.Observable.combineLatest(
  Model.subject,
  RouterObs,
  function (appState, { Handler, State }) {
    return { appState, Handler, State }
})

but now I'm getting this error Uncaught TypeError: location.getCurrentPath is not a function

Thanks for the help so far really appreciated

@BerkeleyTrue
Copy link

Sorry was doing a lot of copy pasta last night. Should be

const subscription = Rx.Observable.combineLatest(
  Model.subject,
  RouterObs,
  ((appState, { Handler, State }) => ({
   appState,
   Handler,
   State
}))

But you correction is also valid.

The issue is RouterObs and ObservableRender should be functions like so

const RouterObs = function(routes, location) {
  return Rx.Observable.create(observer => {
    Router.run(routes, location, (Handler, state) => {
      observer.onNext({ Handler, state });
    });
  });
};

const ObservableRender = function(Comp, DOMcontainer) {
  return new Rx.AnonymousObservable(function (observer) {
    let instance = null;
    instance = React.render(Comp, DOMContainer, (err) => {
      if (err) { return observer.onError(err); }
      if (instance) { observer.onNext(instance); }
    });
    observer.onNext(instance);
  });
};

And then the rest should work.

Let me know if that works for you. I'm also on the Reactiflux Slack group under the same name. You can send me a DM with quick questions. This issue should be closed, though, as it is not a direct issue with react-router.

@Cmdv
Copy link
Author

Cmdv commented May 26, 2015

no worries copy and pasta sounds like a nice meal! hehe

Still no immediate joy but I will go through the examples a little more and see if I can come up with something, if I get too stuck I'll give you a message on Slack for some pointers 😃

And you're right I will close down this issue, I did notice it got a little attention on Twiter so people are interested in the solution which is a good sign that others are experimenting in rx + react combination.

@Cmdv
Copy link
Author

Cmdv commented May 26, 2015

Only reopened to see if anyone else has been able to get rx + react-router to play together. If this not the done thing then let me know and I will re-close.

@Cmdv
Copy link
Author

Cmdv commented May 27, 2015

so solution sat up in <RouteHandler {...this.props} /> so you can pass your state into the handler then you need to call it's props to pass to the rest of the app there.

var App = React.createClass({
  render: function () {
    return (
      <div>
        <header>
          <ul>
            <li><Link to="app">Home</Link></li>
            <li><Link to="about">About</Link></li>
            <li><Link to="login">Login</Link></li>
          </ul>
        </header>
        <RouteHandler {...this.props} />
      </div>
    );
  }
});

@Cmdv Cmdv closed this as completed May 27, 2015
@lock lock bot locked as resolved and limited conversation to collaborators Jan 24, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants