Code Monkey home page Code Monkey logo

Comments (16)

BinaryMuse avatar BinaryMuse commented on August 27, 2024

Now that it's in RC status, I'll definitely get something up soon—I just didn't want to rewrite the example every time a beta was released. :)

In the meantime, if you use FluxMixin in your route handlers, then you can make sure they get your flux instance as a prop by using createElement:

var flux = new Fluxxor.Flux(...);

// make sure react router injects the `flux` prop
// into every component it instantiates
function createFluxComponent(Component, props) {
  return <Component {...props} flux={flux} />;
}

var router = (
  <Router history={history} createElement={createFluxComponent}>

from fluxxor.

andrewslater avatar andrewslater commented on August 27, 2024

Everything seems to work fine with the setup described here but I do see the following warning from React

Warning: owner-based and parent-based contexts differ (values: `undefined` vs `[object Object]`) for key (flux) while mounting module.exports (see: http://fb.me/react-context-by-parent)

Even after reading the page linked to in the warning I'm not sure how to avoid it. Any ideas?

from fluxxor.

BinaryMuse avatar BinaryMuse commented on August 27, 2024

@andrewslater I don't think I've seen that in a while with Fluxxor + React Router. Do you have a small reproducible example I can take a look at?

from fluxxor.

andrewslater avatar andrewslater commented on August 27, 2024

I tried reproducing this in the react-router example by migrating it to use react-router 1.0-rc1 however everything seems to be working fine after I made the changes! I will continue to explore the issue in the codebase I'm working in where this problem remains.

I've submitted a pull request which includes my changes to upgrade to react-router 1.0-rc1 as I imagine that will be something the fluxxor project will want at some point. The API for react-router has changed enough that it did require some fairly widespread changes within the example.

#142

from fluxxor.

FutureKode avatar FutureKode commented on August 27, 2024

Thanks for the example Andrew.

Hey guys, what if I wanted to have a wrapper around all my pages that has the flux instance and some ui like a header. I can't put it in an 'empty-view' since that doesn't have the flux instance right?

from fluxxor.

BinaryMuse avatar BinaryMuse commented on August 27, 2024

@FutureKode In the example Andrew added, every route handler that React Router renders gets the flux instance as this.props.flux (due to the createElement option), so you can just add the FluxMixin and everything will work as normal.

If you wanted some UI, just create a new component (e.g. AppContainer or something) that has the FluxMixin and surrounds all the other routes you want it to contain, e.g.

<Router ...>
  <Route component={AppContainer}>
    /* other routes here */
  </Route>
</Router>

It's possible, though, that EmptyView isn't even useful for this case, and you could just rename/repurpose it for what you wanted. In fact, it's entirely possible it's not necessary at all for React Router 1.0.

Whenever React Router (1.0-rc1+) renders a route handler, it will render all the parent route handlers that contain it, including ones that don't have a path attribute. For example, here's are the routes for a recent project I worked on:

const router = () => {
  return (
    <Router history={history} createElement={createFluxComponent}>
      <Redirect from="/project/:project_id" to="/project/:project_id/tree" />
      <Route component={Application}>
        <Route component={Auth}>
          <Route component={ProjectList} path="/" />
          <Route component={Project} path="project/:project_id">
            <Route components={ProjectSettings}>
              <Route components={{
                generalSettingsEditor: ProjectGeneralSettings,
                hcDataKeysEditor: ProjectHcDataKeysEditor,
                aclEditor: ProjectAclEditor,
                importExport: ProjectImportExport,
                projectDeletePane: ProjectDeletePane
              }} path="/settings" />
            </Route>
            <Route component={ProjectHcBank} path="/bank" />
            <Route component={ProjectTreeEditor} path="/tree" />
          </Route>
        </Route>
      </Route>
    </Router>
  );
}

In this example, going to /project/:project_id/tree would render

<Application>
  <Auth>
    <Project>
      <ProjectTreeEditor />
    </Project>
  </Auth>
</Application>

because the matched route is contained by Application and Auth, even though they don't have path attributes.

from fluxxor.

FutureKode avatar FutureKode commented on August 27, 2024

Thanks man, that works. Only thing is I get

Warning: owner-based and parent-based contexts differ (values: undefinedvs[object Object]) for key (flux) while mounting LandingPage (see: http://fb.me/react-context-by-parent)

I'm probably doing something wrong

from fluxxor.

andrewslater avatar andrewslater commented on August 27, 2024

@FutureKode if you ever figure out how to avoid that warning please let us know. Like you, everything seems to be working fine for me but I've not been able to determine what the cause is (and it drives me crazy seeing it in my js console log!)

from fluxxor.

andrewslater avatar andrewslater commented on August 27, 2024

Upgraded to react 0.14.0 and I no longer get this warning :)

from fluxxor.

FutureKode avatar FutureKode commented on August 27, 2024

Sweet! Thanks for letting us know... time to upgrade my reactjs too :)

from fluxxor.

nosovsh avatar nosovsh commented on August 27, 2024

@andrewslater that just because that warning was removed in 0.14. So absence of waring doesn't mean that there is no bug connected to context ;)

from fluxxor.

BinaryMuse avatar BinaryMuse commented on August 27, 2024

@FutureKode @andrewslater Since the owner tree is a subset of the parent tree, it's possible there was no issue here; React was just informing you that the context from the parent and the context from the owner were different. It might be more obvious if we could see your route config, the entry point into your application, and the component where LandingPage is used.

from fluxxor.

FutureKode avatar FutureKode commented on August 27, 2024

Hey @BinaryMuse , thanks for looking.

routes.js

var React = require("react"),
    ReactRouter = require("react-router"),
    Route = ReactRouter.Route;

var AppContainer = require('./components/app-container.js'),
    LandingPage = require("./components/landing-page.js"),
    MenuPage = require("./components/menu-page.js"),
    PaymentPage = require("./components/payment-page.js"),
    ConfirmationPage = require("./components/confirmation-page.js"),
    ClosedPage = require("./components/closed-page.js");

var routes = (
  <Route component={AppContainer}>
    <Route component={LandingPage} path="/" />
    <Route component={ClosedPage} path="/closed" />
    <Route component={ConfirmationPage} path="/confirmation" />
    <Route component={MenuPage} path="/menu/:restaurant_id" />
    <Route component={PaymentPage} path="/payment" />
  </Route>
);

module.exports = routes;

app.js

var React = require('react'),
    ReactRouter = require("react-router"),
    Fluxxor = require('fluxxor');

var actions = require("./actions"),
    routes = require('./routes'),
    AppStore = require("./stores/app_store"),
    OrderStore = require("./stores/order_store"),
    RouteStore = require("./stores/route_store"),
    UserStore = require("./stores/user_store");

var Router = ReactRouter.Router;

var createBrowserHistory = require('history/lib/createBrowserHistory');
var history = createBrowserHistory();

var stores = {
  AppStore: new AppStore(),
  OrderStore: new OrderStore(),
  RouteStore: new RouteStore({router: Router, history: history}),
  UserStore: new UserStore()
};

var flux = new Fluxxor.Flux(stores, actions);
flux.on("dispatch", function(type, payload) {
  console.log("Dispatch:", type, payload);
});

var createElement = function(Component, props) {
  return <Component {...props} flux={flux} />
}

React.render(
  <Router createElement={createElement} history={history} routes={routes} />,
  document.getElementById('app')
);

landing-page.js

var React = require("react"),
    Fluxxor = require('fluxxor'),
    FluxMixin = Fluxxor.FluxMixin(React),
    StoreWatchMixin = Fluxxor.StoreWatchMixin;

var Footer = require('./footer'),
    RestaurantSelect = require('./restaurant-select'),
    PostcodeInput = require('./postcode-input');

var geolocation = require('geolocation');

var LandingPage = React.createClass({
  mixins: [
    FluxMixin,
    StoreWatchMixin("AppStore", "OrderStore", "UserStore")
  ],
  componentWillMount: function() {
    document.body.id = 'landing_page';
  },
  getStateFromFlux: function() {
    var flux = this.props.flux;
    return {
      app: flux.store("AppStore"),
      order: flux.store("OrderStore"),
      user: flux.store("UserStore")
    };
  },
  getUserLocation: function() {
    geolocation.getCurrentPosition(function (err, position) {
      if(err) console.log(err);
      else {
        this.getFlux().actions.user.setLocation({
          lat: position.coords.latitude,
          lng: position.coords.longitude
        });
      }
    }.bind(this));
  },
  render: function() {

    var deliverClasses = 'module hidden';
    if(this.state.app.doesDeliver === true) deliverClasses = 'module';

    return (
      <div>

       // some stuff in here

      </div>
    )
  }
});

module.exports = LandingPage;

route_store.js

Is it correct to pass history into here from app.js, like: RouteStore: new RouteStore({router: Router, history: history}) ?

var Fluxxor = require('fluxxor'),
    Constants = require("../constants");

var actions = require("../actions");

var RouteStore = Fluxxor.createStore({

  initialize: function(options) {
    this.router = options.router;
    this.history = options.history;
    this.bindActions(
      Constants.ROUTE_TRANSITION, this.handleRouteTransition
    );
  },

  handleRouteTransition: function(payload) {
    var path = payload.path,
        params = payload.params;
    this.history.pushState(null, path, params);
    this.emit("change");
  }
});

module.exports = RouteStore;

from fluxxor.

BinaryMuse avatar BinaryMuse commented on August 27, 2024

@FutureKode Okay, here's what's happening:

In React 0.13, React warned you about the differences between owner context and parent context. The definitions are:

  • Owner context is the context provided by the thing that created you
  • Parent context is the context provided by the component you're nested inside

I like to think of them as "context at instantiation time" and "context at render time."

In React Router <1.0, you (the user) were responsible for creating the component instance:

var MyComponent = React.createClass({
  mixins: [FluxMixin],
  render() {
    return <RouteHandler />;
  }
});

Since you both instantiated and also rendered the component referenced by RouteHandler (in our case, LandingPage), the owner context and the parent context are both provided by MyComponent—no warnings.

In React Router >=1.0, you (the user) simply receive new route handler as already instantiated components:

var MyComponent = React.createClass({
  mixins: [FluxMixin],
  render() {
    return <div>{this.props.children}</div>;
  }
});

That means some other entity (the router) created an instance of <LandingPage /> and passed it to you as this component's props.children; you aren't the owner, the router is; thus, LandingPage's owner context is the context provided by the router instance, and not by your component. However, since you rendered this.props.children, MyComponent is the parent, and so its parent context is what you'd expect, and the owner and parent contexts differ.

In React 0.13, to fix this, you'd need to change it so that the router inherited your own defined context by rendering it inside a component you owned:

const App = React.createClass({
  mixins: [FluxMixin],
  render() {
    return <Router createElement={createElement} routes={routes} />;
  }
})

React.render(
  <App flux={flux} />, document.getElementById('app')
);

Now, the router's owner and parent context are defined by App, since you both created and also rendered the router inside your own component. However, this workaround is not necessary in 0.14, since React now uses the parent context.

The reason this wasn't an issue in your case is because you're using getFlux(), which defaults to this.props.flux if it's available before looking on the context. In fact, I'd go so far as to say you don't need FluxMixin at all, since you have access to the Fluxxor instance on this.props.flux (you'd just need to change this.getFlux() to this.props.flux); if you need to access it in a sub-component (e.g. a non-route-handler), just pass it as a prop.

from fluxxor.

nosovsh avatar nosovsh commented on August 27, 2024

@BinaryMuse thank you for explanation

from fluxxor.

FutureKode avatar FutureKode commented on August 27, 2024

@BinaryMuse Yes, thanks a lot for this excellent explanation. Your fix worked for React 0.13 && the warning doesn't show for React 0.14 with the way I had it :)

from fluxxor.

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.