This bit is shared among all approaches, the actual prop definition.
var Actions = require('actions');
var CourseStore = require('stores/CourseStore');
var asyncProps = {
course: {
// We load the course from a remote backend and trigger a redirect to
// a different page if the resource was not found.
//
// Also, we need a routing parameter (`:courseId`) to fetch the resource.
//
// This is handled in an action that dispatches the XHR and interacts with
// the router to do the redirect if necessary.
fetch: function(info) {
return Actions.fetchCourseAndRedirectIfNotFound(info.params.courseId);
},
// We don't necessarily want to use the raw version of the payload we
// received in `#fetch()`; the store has a different version that we
// should use instead, so we specify the actual value we want to assign
// to the prop in `#get()`.
get: function(info) {
return CourseStore.get(info.params.courseId);
},
setup: function(onChange) {
CourseStore.addChangeListener(onChange);
},
teardown: function(onChange) {
CourseStore.removeChangeListener(onChange);
}
}
};
All approaches need to:
- provide the resolved async props to the component as regular
props
; the implementation should not be aware of the source of the prop, so that we can easily swap them in/avoid the remote backend when testing or doing funny things like previewing
This mixin keeps updating the host component as props are resolved, and provides a hook to test whether any props are still being resolved.
Resolved props are injected/hacked into this.props
using a MutableProps
implementation.
var CourseHandler = React.createClass({
mixins: [
AsyncPropResolver(asyncProps)
],
render() {
if (this.isResolvingAsyncProps()) {
return null;
}
var { course } = this.props;
var features = {
manualMessaging: isFeatureEnabled("manual_messaging")
};
return (
<CourseView course={course} features={features} />
);
}
});
- transparent;
this.props
gets mutated with the resolved props - easily testable; simply pass the prop and we won't have to resolve it
- the component has access to the loading state and can react to it since it's mounted as props are being resolved
- the mutation of
this.props
is not very appealing
var CourseHandler = React.createClass({
mixins: [
BlockingPropResolver(asyncProps)
],
render() {
var { course } = this.props;
var features = {
manualMessaging: isFeatureEnabled("manual_messaging")
};
return (
<CourseView course={course} features={features} />
);
}
});
- transparent;
this.props
gets mutated with the resolved props - easily testable; simply pass the prop and we won't have to resolve it
- is able to stop the transition (and the URL bar from updating) until the props are resolved; if something goes bad, the URL will be left intact
- you can't render a loading state (obviously)
- like
AsyncPropResolver
, the mutation ofthis.props
is not very appealing
Pass down the asyncProps
to this component along with a content renderer and
it will resolve the props and pass them to the renderer you specify. Every-time
a change occurs, the renderer is re-invoked.
var CourseHandler = React.createClass({
render() {
return(
<PropResolver
asyncProps={asyncProps}
params={this.props.params}
query={this.props.query}
render={this.renderContent}
/>
);
},
renderContent(props, isResolving) {
var { course } = props;
var features = {
manualMessaging: isFeatureEnabled("manual_messaging")
};
return (
<CourseView course={course} features={features} />
);
}
});
- doesn't do anything funny like mutating
this.props
- easy to reason about and simple to implement
- can not intercept a transition, does not have access to route-handler states/hooks
- not easy to test