Skip to content

Question - Component Distribution #547

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

Closed
nigelsmith opened this issue Nov 30, 2014 · 5 comments
Closed

Question - Component Distribution #547

nigelsmith opened this issue Nov 30, 2014 · 5 comments

Comments

@nigelsmith
Copy link

With the old API it seemed relatively clear that a component built using react-router could be distributed and used in the ordinary way by someone simply requiring it and using React.render to mount it into the DOM.

With Router.run though it's not clear to me what the best way of distributing the component would be. If someone consumes my component I'd like them to use it like any other react component without having to call a special method to begin routing.

Is the way to achieve this using a wrapper component with Router.run being started in ComponentDidMount?

That way a user could attach the component to the DOM without knowing anything about the react-router API but all the component would do would be to call React.render itself and start routing internally.

@gaearon
Copy link
Contributor

gaearon commented Dec 3, 2014

What kind of component is it? It's quite unordinary that a component starts to manage browser history when it is mounted, unless it's a router itself. What happens if two such components are mounted at the same time?

You can definitely call run inside your componentDidMount, more or less this way:

var RouterWrapper = React.createClass({
  getInitialState: function () {
    return {
      routerState: null,
      routeHandler: null
    };
  },

  componentDidMount: function () {
    Router.run(this.handleRouteChange);
  },

  handleRouteChange: function(routeHandler, routerState) {
    if (this.isMounted()) {
      return;
    }

    this.setState({
      routeHandler: routeHandler,
      routerState: routerState
    });
  },

  render: function () {
    if (!this.state.routeHandler) {
      return null;
    }

    var RouteHandler = this.state.routeHandler;
    return <RouteHandler {...this.state.routerState} />;
  }
});

I don't think there's a way to tear down a router once it's running though. And again, I doubt it will work with multiple routers on one page. So component doesn't seem like the best abstraction for this anyway.

@nigelsmith
Copy link
Author

Thanks for the reply @gaearon - what you suggest is more or less what I've done now; I agree that it wouldn't make sense for two components to have a fight over the window location :)

What bothers me is that react has a nicely declarative and compositional style whereas having the equivalent of a 'main' function in run switches it to something imperative. If someone uses a component that builds a complete interface I'd like them to just know how to use react without having to tell them to call a router function or my own wrapper function that under the hood kicks off the routing cycle.

@gaearon
Copy link
Contributor

gaearon commented Dec 3, 2014

What is your use case? What are you building?

What bothers me is that react has a nicely declarative and compositional style whereas having the equivalent of a 'main' function in run switches it to something imperative.

New API is a win for everyone who wants to control rendering. It enables stuff like this:

var resolveHash = require('when/keys').all;

var SampleHandler = React.createClass({
  statics: {
    // this is going to be called in the `run` callback
    fetchData: function (params) {
      return fetchStuff(params);
    }
  },
  // ...
});

Router.run(routes, Router.HistoryLocation, function (Handler, state) {

  // create the promises hash
  var promises = state.routes.filter(function (route) {
    // gather up the handlers that have a static `fetchData` method
    return route.handler.fetchData;
  }).reduce(function (promises, route) {
    // reduce to a hash of `key:promise`
    promises[route.name] = route.handler.fetchData(state.params);
    return promises;
  }, {});

  resolveHash(promises).then(function (data) {
    // wait until we have data to render, the old screen stays up until
    // we render
    React.render(<Handler data={data}/>, document.body);
  });
});

If you want to encapsulate this in component, you can (as I described above) but it's a leaky abstraction because a router is more akin to observable that tells you what to render, not something to render per se.

@nigelsmith
Copy link
Author

Consider a component that generates routes based on parameters passed to it. Should the user of that component have to kick routing off with a method?

I appreciate that there are advantages to the new approach though whether a special method with a callback is quite in line with the way a lot of react/flux apps are structured I'm less certain. In any event I'll continue to experiment with using componentDidMount so I'll close the issue. Thanks for the help @gaearon

@gaearon
Copy link
Contributor

gaearon commented Dec 3, 2014

Consider a component that generates routes based on parameters passed to it. Should the user of that component have to kick routing off with a method?

Sorry, I'm still not sure what your use case is so this leaves me confused. Are you building something like a website builder?

whether a special method with a callback is quite in line with the way a lot of react/flux apps are structured I'm less certain

Every React app, Flux or not, has a React.render call at the top. With react-router, you replace this one line with a couple of lines that not just call render once, but whenever route changes. Certainly it's not meant to be used somewhere in the middle of the hierarchy. It also works well in Flux app. In fact its “imperative” nature is very similar to how stores trigger changes on top level “controller view” components.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 25, 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

2 participants