UNPKG

react-injectables

Version:

Explicitly inject Components into any part of your React render tree.

195 lines (151 loc) 5.65 kB
import { Children, Component, PropTypes } from 'react'; import { compose, find, filter, map, uniqBy, without, withoutAll } from './utils'; import invariant from 'invariant'; class InjectablesProvider extends Component { static childContextTypes = { registerInjector: PropTypes.func.isRequired, removeInjector: PropTypes.func.isRequired, updateInjector: PropTypes.func.isRequired, registerInjectable: PropTypes.func.isRequired, removeInjectable: PropTypes.func.isRequired, }; static propTypes = { children: PropTypes.element }; constructor(props, context) { super(props, context); this.registrations = []; } getChildContext() { return { registerInjector: (args) => this.registerInjector(args), removeInjector: (args) => this.removeInjector(args), updateInjector: (args) => this.updateInjector(args), registerInjectable: (args) => this.registerInjectable(args), removeInjectable: (args) => this.removeInjectable(args) }; } getRegistration(args) { const { injectionId } = args; let registration = find( x => x.injectionId === injectionId )(this.registrations); if (!registration) { registration = { injectionId, injectables: [], injections: [] }; this.registrations.push(registration); } return registration; } runInjections(args) { const { registration } = args; const { injectables, injections } = registration; const elements = compose( filter(x => x !== null && x !== undefined), map(x => x.inject()), uniqBy(`injectorId`) )(injections); injectables.forEach(injectable => { injectable.receive(elements); }); } removeRegistration(args) { const { registration } = args; this.registrations = without(registration)(this.registrations); } findInjectable({ registration, injectableInstance }) { const isInjectableInstance = x => Object.is(x.instance, injectableInstance); return find(isInjectableInstance)(registration.injectables); } clearRegistrationIfEmpty({ registration }) { if (registration.injectables.length === 0 && registration.injections.length === 0) { this.removeRegistration({ registration }); } } registerInjectable(args) { const { injectionId, injectableInstance, receive } = args; const registration = this.getRegistration({ injectionId }); const injectable = this.findInjectable( { registration, injectableInstance }); if (withoutAll(registration.injectables)([injectable]).length > 0) { const newInjectable = { instance: injectableInstance, receive }; registration.injectables = [...registration.injectables, newInjectable]; this.runInjections({ registration }); // First time consumption. } } removeInjectable(args) { const { injectionId, injectableInstance } = args; const registration = this.getRegistration({ injectionId }); const injectable = this.findInjectable( { registration, injectableInstance }); registration.injectables = without(injectable)(registration.injectables); this.clearRegistrationIfEmpty({ registration }); } findInjection({ registration, injectorInstance }) { const isInjectorInstance = x => Object.is(x.instance, injectorInstance); return find(isInjectorInstance)(registration.injections); } findInjector({ registration, injectorId }) { const isInjectorId = x => x.injectorId === injectorId; return find(isInjectorId)(registration.injections); } registerInjector(args) { const { injectionId, injectorId, injectorInstance, inject } = args; const registration = this.getRegistration({ injectionId }); const existingInjection = this.findInjection({ registration, injectorInstance }); invariant( !existingInjection, `An Injector instance is being registered multiple times.`); if (process.env.NODE_ENV !== `production`) { const existingInjector = this.findInjector({ registration, injectorId }); if (existingInjector && console && console.warn) { // eslint-disable-line no-console console.warn( // eslint-disable-line no-console `Multiple instances of an Injector has been found. This may not be ` + `your intended behaviour`); } } const newInjection = { injectorId, instance: injectorInstance, inject }; registration.injections = [ ...registration.injections, newInjection ]; this.runInjections({ registration }); } updateInjector(args) { const { injectionId, injectorInstance, inject } = args; const registration = this.getRegistration({ injectionId }); const existingInjection = this.findInjection({ registration, injectorInstance }); invariant( existingInjection, `Trying to update an Injector that is not registered`); existingInjection.inject = inject; this.runInjections({ registration }); } removeInjector(args) { const { injectionId, injectorInstance } = args; const registration = this.getRegistration({ injectionId }); const injection = this.findInjection({ registration, injectorInstance }); invariant( !!injection, `Trying to remove an injector which has not been registered`); registration.injections = without(injection)(registration.injections); this.runInjections({ registration }); this.clearRegistrationIfEmpty({ registration }); } render() { return ( Children.only(this.props.children) ); } } export default InjectablesProvider;