Comments (16)
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.
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.
@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.
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.
from fluxxor.
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.
@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.
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.
@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.
Upgraded to react 0.14.0 and I no longer get this warning :)
from fluxxor.
Sweet! Thanks for letting us know... time to upgrade my reactjs too :)
from fluxxor.
@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.
@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.
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.
@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.
@BinaryMuse thank you for explanation
from fluxxor.
@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)
- resetting stores HOT 2
- Question: asynchronous route transition HOT 1
- Inconsistent description of stores? HOT 4
- Fluxxor and React Router 1.x and ES6 HOT 7
- this.isMounted is not a function HOT 2
- Best practices for nested models HOT 1
- React warning after update to 1.7.2 HOT 1
- Can one use Fluxxor with React Native? HOT 2
- Use instanceOf or shape in context types and add propTypes
- Wait until async request completes HOT 2
- Routing examples no longer work with latest version of React Router HOT 3
- Lost events with StoreWatchMixin during ComponentWillMount -> ComponentDidMount HOT 1
- Documentation for setting dispatch interceptor is out of date HOT 3
- In the Async Example not clear how to support add action which needed to call out to server HOT 1
- React-Router v4 and Fluxxor
- Current lodash dependency has a CVE
- Compatibility with React 16 HOT 2
- Typo in a link on the "What is Flux" page
- sdasdasdas
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from fluxxor.