import * as React from 'react'; import { ComponentProvider } from 'react-native'; import { polyfill } from 'react-lifecycles-compat'; import hoistNonReactStatics from 'hoist-non-react-statics'; import { Store } from './Store'; import { ComponentEventsObserver } from '../events/ComponentEventsObserver'; interface HocState { componentId: string; allProps: {}; } interface HocProps { componentId: string; } export interface IWrappedComponent extends React.Component { setProps(newProps: Record, callback?: () => void): void; isMounted: boolean; } export class ComponentWrapper { wrap( componentName: string | number, OriginalComponentGenerator: ComponentProvider, store: Store, componentEventsObserver: ComponentEventsObserver, concreteComponentProvider: ComponentProvider = OriginalComponentGenerator, ReduxProvider?: any, reduxStore?: any ): React.ComponentClass { const GeneratedComponentClass = OriginalComponentGenerator(); class WrappedComponent extends React.Component { static getDerivedStateFromProps(nextProps: any, prevState: HocState) { return { allProps: { ...nextProps, ...store.getPropsForId(prevState.componentId), }, }; } private _isMounted = false; get isMounted() { return this._isMounted; } constructor(props: HocProps) { super(props); this._assertComponentId(); this.state = { componentId: props.componentId, allProps: {}, }; store.setComponentInstance(props.componentId, this); } public setProps(newProps: any, callback?: () => void) { this.setState( (prevState) => ({ allProps: { ...prevState.allProps, ...newProps, }, }), callback ); } componentDidMount() { this._isMounted = true; store.consumePendingProps(this.state.componentId); } componentWillUnmount() { store.clearComponent(this.state.componentId); componentEventsObserver.unmounted(this.state.componentId); } render() { return ( ); } private _assertComponentId() { if (!this.props.componentId) { throw new Error(`Component ${componentName} does not have a componentId!`); } } } polyfill(WrappedComponent); hoistNonReactStatics( WrappedComponent, concreteComponentProvider === OriginalComponentGenerator ? GeneratedComponentClass : concreteComponentProvider() ); return ReduxProvider ? this.wrapWithRedux(WrappedComponent, ReduxProvider, reduxStore) : WrappedComponent; } wrapWithRedux( WrappedComponent: React.ComponentClass, ReduxProvider: any, reduxStore: any ): React.ComponentClass { class ReduxWrapper extends React.Component { render() { return ( ); } } hoistNonReactStatics(ReduxWrapper, WrappedComponent); return ReduxWrapper; } }