import * as React from 'react'; import withContext from 'recompose/withContext'; /** * Creates a function which validates that a property of an object is an instance of a specific class. * For use with React.Component.contextTypes. */ function createValidator( classDef: new (...args: any[]) => any ): React.Validator { return function validator( object: T, key: string, componentName: string ): Error | null { if (key in object && object[key as keyof T] instanceof classDef) return null; return new Error( `Expecting ${componentName} to receive {${JSON.stringify(key)}: ${ classDef.name }} from context` ); }; } /** * This can be used as a decorator to make a component provide a store context property to its children. * Usage: * * @providesStoreContext(MyStore) * export class MyClass extends React.Component<P, S> { } * */ export function providesStoreContext( storeClass: new (..._: any[]) => Store ) { return withContext( { store: createValidator(storeClass) }, ({ store }: { store: Store }) => ({ store }) ); } /** * Extends React.Component by adding a store getter which reads the store context property. * Usage: class MyComponent extends ComponentGetsStoreContext(MyStore) { } */ export function ComponentGetsStoreContext( storeClass: new (..._: any[]) => Store ) { return class ComponentGetsStoreContext extends React.Component { static contextTypes = { store: createValidator(storeClass), }; get store(): Store { return this.context.store; } }; }