Hi,
More a "how to" issue. I don't understand how exactly I have to use FluxibleMixin
and FluxibleComponent
to propagate the context
in all the descendant views.
Here comes a lot of code, but that's for illustrating my use case and maybe it could be useful to some others.
Please note that I use Fluxible only client-side for this project.
Here is my main app:
'use strict';
var React = require('react');
var Fluxible = require('fluxible');
var EntryPointController = require('./entrypoint/EntryPointController.react');
var EntryPointStore = require('./entrypoint/EntryPointStore');
var CreationStore = require('./creation/CreationStore');
var PhotosStore = require('./photos/PhotosStore');
var app = new Fluxible({
component: React.createFactory(EntryPointController)
});
app.registerStore(EntryPointStore);
app.registerStore(CreationStore);
app.registerStore(PhotosStore);
var context = app.createContext();
React.render(
app.getComponent()({
context: context.getComponentContext()
}),
document.getElementById('app')
);
EntryPointController
is basically a page asking a question where "Yes" render a Controller (creation), and "No" render another one (simple album). Here is a simplified version:
var EntryPointController = React.createClass({
mixins: [FluxibleMixin],
statics: {
storeListeners: [EntryPointStore]
},
getInitialState: function() {
return this.getState();
},
getState: function() {
this.store = this.getStore(EntryPointStore);
return {
creationType: this.store.getCreationType()
};
},
// UI event handlers
createCreation: function() {
this.executeAction(EntryPointActions.createCreation, {});
},
createAlbum: function() {
this.executeAction(EntryPointActions.createAlbum, {});
},
render: function() {
if ('creation' === this.state.creationType) {
return (
<CreationController/>
);
}
if ('album' === this.state.creationType) {
// TODO
return (
<AlbumController/>
);
}
return (
<div>
<p className="question">
Would you like to introduce one or your creation{'\u003F'}
</p>
<button type="button" onClick={this.createCreation}>Yes!</button>
<button type="button" onClick={this.createAlbum}>No</button>
</div>
);
},
onChange: function () {
this.setState(this.getState());
}
});
module.exports = EntryPointController;
Now, CreationController
is a multiple-step form, each step being a simple view (meaning it doesn't dispatch any action, nor manage the global state of the form). All steps relay on the controller regarding the global state management (in CreateStore).
But in one of these views, I render a PhotosController
, which is basically a full-featured uploader and image manager, and it has its own store and own state. And that's where I have an issue. I can't seem to be able to propagate the context to it, and I don't understand why.
In my CreationStepThree
view, I added the FluxibleMixin
so the component gets the context
. And it does, but I'm not sure why, it doesn't propagate it to the underlaying PhotosController
component.
var CreationStepThree = React.createClass({
mixins: [FluxibleMixin],
propTypes: {
formData: React.PropTypes.object.isRequired,
onSubmit: React.PropTypes.func.isRequired,
onBack: React.PropTypes.func.isRequired
},
goBack: function(e) {
e.preventDefault();
this.props.onBack();
},
handleSubmit: function(e) {
e.preventDefault();
// todo
this.props.onSubmit({});
},
render: function() {
var formData = this.props.formData;
return (
<PhotosController onFormSubmit={this.handleSubmit} onGoBack={this.goBack} />
);
}
});
module.exports = CreationStepThree;
PhotosController, in part:
var PhotosController = React.createClass({
mixins: [FluxibleMixin],
propTypes: {
onFormSubmit: React.PropTypes.func.isRequired,
onGoBack: React.PropTypes.func.isRequired
},
statics: {
storeListeners: [PhotosStore]
},
getInitialState: function() {
return this.getState();
},
getState: function() {
this.store = this.getStore(PhotosStore);
return {
photos: this.store.getPhotos(),
selectedPhotos: this.store.getSelectedPhotos()
};
},
handleFileAdded: function(photo) {
var id = uuid.v4();
this.executeAction(PhotosActions.addPhoto, {
id: id,
name: photo.name,
type: photo.type,
status: photo.status,
size: photo.size
});
photo.id = id;
},
// a bunch of other handling methods like handleFileAdded
// which dispatch PhotosActions
render: function() {
return (
<div>
...... a lot of stuff here
</div>
);
},
onChange: function () {
this.setState(this.getState());
}
});
module.exports = PhotosController;
Here is the CreationController
:
var CreationController = React.createClass({
mixins: [FluxibleMixin],
statics: {
storeListeners: [CreationStore]
},
getInitialState: function() {
return this.getState();
},
getState: function() {
this.store = this.getStore(CreationStore);
return {
formData: this.store.getFormData(),
currentStep: this.store.getCurrentStep()
};
},
goBack: function() {
this.executeAction(CreationActions.goBack, {});
},
submitStepOne: function(data) {
this.executeAction(CreationActions.submitStepOne, data);
},
submitStepTwo: function(data) {
this.executeAction(CreationActions.submitStepTwo, data);
},
submitStepThree: function(data) {
this.executeAction(CreationActions.submitStepThree, data);
},
render: function() {
var form;
switch(this.state.currentStep) {
case 1:
return (
<CreationStepOne
formData={this.state.formData}
onSubmit={this.submitStepOne}
onBack={this.goBack}
/>
);
break;
case 2:
return (
<CreationStepTwo
formData={this.state.formData}
onSubmit={this.submitStepTwo}
onBack={this.goBack}
/>
);
break;
case 3:
return (
<CreationStepThree
formData={this.state.formData}
onSubmit={this.submitStepThree}
onBack={this.goBack}
/>
);
break;
default:
// todo: handle this case
}
return (
<div>
</div>
);
},
onChange: function () {
this.setState(this.getState());
}
});
module.exports = CreationController;
Any idea ? What am I missing here ? :p
I didn't have any trouble before upgrading to React 0.13.1 & Fluxible 0.3.x.
Also, it may not be related at all, but I now get the following warnings in the console (and not just for CreateStepThree, for all of them):
Warning: owner-based and parent-based contexts differ (values: `function componentExecuteAction(action, payload) {
self.executeAction(action, payload, function actionHandlerWrapper(err) {
var noop = function () {};
self.executeAction(self._app._componentActionHandler, { err: err }, noop);
});
}` vs `undefined`) for key (executeAction) while mounting CreateStepThree (see: http://fb.me/react-context-by-parent)
Warning: owner-based and parent-based contexts differ (values: `function () { [native code] }` vs `undefined`) for key (getStore) while mounting CreateStepThree (see: http://fb.me/react-context-by-parent)