Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| import React, { PropTypes, createElement } from 'react'; | |
| import { Observable, CompositeDisposable } from 'rx'; | |
| import debug from 'debug'; | |
| // interface contain { | |
| // (options?: Object, Component: ReactComponent) => ReactComponent | |
| // (options?: Object) => (Component: ReactComponent) => ReactComponent | |
| // } | |
| // | |
| // Action: { type: String, payload: Any, ...meta } | |
| // | |
| // ActionCreator(...args) => Observable | |
| // | |
| // interface options { | |
| // fetchAction?: ActionCreator, | |
| // getActionArgs?(props: Object, context: Object) => [], | |
| // isPrimed?(props: Object, context: Object) => Boolean, | |
| // handleError?(err) => Void | |
| // shouldRefetch?( | |
| // props: Object, | |
| // nextProps: Object, | |
| // context: Object, | |
| // nextContext: Object | |
| // ) => Boolean, | |
| // } | |
| const log = debug('fcc:professerx'); | |
| function getChildContext(childContextTypes, currentContext) { | |
| const compContext = { ...currentContext }; | |
| // istanbul ignore else | |
| if (!childContextTypes || !childContextTypes.professor) { | |
| delete compContext.professor; | |
| } | |
| return compContext; | |
| } | |
| const __DEV__ = process.env.NODE_ENV !== 'production'; | |
| export default function contain(options = {}, Component) { | |
| /* istanbul ignore else */ | |
| if (!Component) { | |
| return contain.bind(null, options); | |
| } | |
| let action; | |
| let isActionable = false; | |
| let hasRefetcher = typeof options.shouldRefetch === 'function'; | |
| const getActionArgs = typeof options.getActionArgs === 'function' ? | |
| options.getActionArgs : | |
| (() => []); | |
| const isPrimed = typeof typeof options.isPrimed === 'function' ? | |
| options.isPrimed : | |
| (() => false); | |
| return class Container extends React.Component { | |
| constructor(props, context) { | |
| super(props, context); | |
| this.__subscriptions = new CompositeDisposable(); | |
| } | |
| static displayName = `Container(${Component.displayName})`; | |
| static propTypes = Component.propTypes; | |
| static contextTypes = { | |
| ...Component.contextTypes, | |
| professor: PropTypes.object | |
| }; | |
| componentWillMount() { | |
| const { professor } = this.context; | |
| const { props } = this; | |
| if (!options.fetchAction) { | |
| log(`${Component.displayName} has no fetch action defined`); | |
| return null; | |
| } | |
| action = props[options.fetchAction]; | |
| isActionable = typeof action === 'function'; | |
| if (__DEV__ && typeof action !== 'function') { | |
| throw new Error( | |
| `${options.fetchAction} should return a function but got ${action}. | |
| Check the fetch options for ${Component.displayName}.` | |
| ); | |
| } | |
| if ( | |
| !professor || | |
| !professor.fetchContext | |
| ) { | |
| log( | |
| `${Component.displayName} did not have professor defined on context` | |
| ); | |
| return null; | |
| } | |
| const actionArgs = getActionArgs( | |
| props, | |
| getChildContext(Component.contextTypes, this.context) | |
| ); | |
| professor.fetchContext.push({ | |
| name: options.fetchAction, | |
| action, | |
| actionArgs, | |
| component: Component.displayName || 'Anon' | |
| }); | |
| } | |
| componentDidMount() { | |
| if (isPrimed(this.props, this.context)) { | |
| log('container is primed'); | |
| return null; | |
| } | |
| if (!isActionable) { | |
| log(`${Component.displayName} container is not actionable`); | |
| return null; | |
| } | |
| const actionArgs = getActionArgs(this.props, this.context); | |
| const fetch$ = action.apply(null, actionArgs); | |
| if (__DEV__ && !Observable.isObservable(fetch$)) { | |
| console.log(fetch$); | |
| throw new Error( | |
| `Action creator should return an Observable but got ${fetch$}. | |
| Check the action creator for fetch action ${options.fetchAction}` | |
| ); | |
| } | |
| const subscription = fetch$.subscribe( | |
| () => {}, | |
| options.handleError | |
| ); | |
| this.__subscriptions.add(subscription); | |
| } | |
| componentWillReceiveProps(nextProps, nextContext) { | |
| if ( | |
| !isActionable || | |
| !hasRefetcher || | |
| !options.shouldRefetch( | |
| this.props, | |
| nextProps, | |
| getChildContext(Component.contextTypes, this.context), | |
| getChildContext(Component.contextTypes, nextContext) | |
| ) | |
| ) { | |
| return; | |
| } | |
| const actionArgs = getActionArgs( | |
| this.props, | |
| getChildContext(Component.contextTypes, this.context) | |
| ); | |
| const fetch$ = action.apply(null, actionArgs); | |
| if (__DEV__ && Observable.isObservable(fetch$)) { | |
| throw new Error( | |
| 'fetch action should return observable' | |
| ); | |
| } | |
| const subscription = fetch$.subscribe( | |
| () => {}, | |
| options.errorHandler | |
| ); | |
| this.__subscriptions.add(subscription); | |
| } | |
| componentWillUnmount() { | |
| if (this.__subscriptions) { | |
| this.__subscriptions.dispose(); | |
| } | |
| } | |
| shouldComponentUpdate() { | |
| // props should be immutable | |
| return false; | |
| } | |
| render() { | |
| const { props } = this; | |
| return createElement( | |
| Component, | |
| props | |
| ); | |
| } | |
| }; | |
| } |