diff --git a/lib/src/commands/Commands.ts b/lib/src/commands/Commands.ts index 792fcbb2e0c..96bfb184b51 100644 --- a/lib/src/commands/Commands.ts +++ b/lib/src/commands/Commands.ts @@ -1,5 +1,5 @@ -import cloneDeep from 'lodash/cloneDeep' -import map from 'lodash/map' +import cloneDeep from 'lodash/cloneDeep'; +import map from 'lodash/map'; import { CommandsObserver } from '../events/CommandsObserver'; import { NativeCommandsSender } from '../adapters/NativeCommandsSender'; import { UniqueIdProvider } from '../adapters/UniqueIdProvider'; diff --git a/lib/src/components/ComponentRegistry.test.tsx b/lib/src/components/ComponentRegistry.test.tsx index a9e64b4e607..860bcdffb8a 100644 --- a/lib/src/components/ComponentRegistry.test.tsx +++ b/lib/src/components/ComponentRegistry.test.tsx @@ -5,14 +5,19 @@ import { ComponentWrapper } from './ComponentWrapper'; import { ComponentEventsObserver } from '../events/ComponentEventsObserver'; import { AppRegistryService } from '../adapters/AppRegistryService'; import { ComponentProvider } from 'react-native'; +import * as React from 'react'; const DummyComponent = () => null; +class MyComponent extends React.Component {} + describe('ComponentRegistry', () => { let mockedStore: Store; let mockedComponentEventsObserver: ComponentEventsObserver; let mockedComponentWrapper: ComponentWrapper; let mockedAppRegistryService: AppRegistryService; + let componentWrapper: ComponentWrapper; + let store: Store; let uut: ComponentRegistry; beforeEach(() => { @@ -20,11 +25,13 @@ describe('ComponentRegistry', () => { mockedComponentEventsObserver = mock(ComponentEventsObserver); mockedComponentWrapper = mock(ComponentWrapper); mockedAppRegistryService = mock(AppRegistryService); + store = instance(mockedStore); + componentWrapper = instance(mockedComponentWrapper); uut = new ComponentRegistry( - instance(mockedStore), + store, instance(mockedComponentEventsObserver), - instance(mockedComponentWrapper), + componentWrapper, instance(mockedAppRegistryService) ); }); @@ -48,4 +55,17 @@ describe('ComponentRegistry', () => { uut.registerComponent('example.MyComponent.name', generator); expect(generator).toHaveBeenCalledTimes(0); }); + + it('should wrap component only once', () => { + componentWrapper.wrap = jest.fn(); + let wrappedComponent: React.ComponentClass; + store.hasRegisteredWrappedComponent = jest.fn(() => wrappedComponent !== undefined); + store.setWrappedComponent = jest.fn(() => wrappedComponent = MyComponent); + + const generator: ComponentProvider = jest.fn(() => DummyComponent); + uut.registerComponent('example.MyComponent.name', generator)(); + uut.registerComponent('example.MyComponent.name', generator)(); + + expect(componentWrapper.wrap).toHaveBeenCalledTimes(1); + }); }); diff --git a/lib/src/components/ComponentRegistry.ts b/lib/src/components/ComponentRegistry.ts index 5ad649f6638..a0c188550e6 100644 --- a/lib/src/components/ComponentRegistry.ts +++ b/lib/src/components/ComponentRegistry.ts @@ -20,15 +20,21 @@ export class ComponentRegistry { reduxStore?: any ): ComponentProvider { const NavigationComponent = () => { - return this.componentWrapper.wrap( - componentName.toString(), - componentProvider, - this.store, - this.componentEventsObserver, - concreteComponentProvider, - ReduxProvider, - reduxStore - ); + if (this.store.hasRegisteredWrappedComponent(componentName)) { + return this.store.getWrappedComponent(componentName); + } else { + const wrappedComponent = this.componentWrapper.wrap( + componentName.toString(), + componentProvider, + this.store, + this.componentEventsObserver, + concreteComponentProvider, + ReduxProvider, + reduxStore + ); + this.store.setWrappedComponent(componentName, wrappedComponent); + return wrappedComponent; + } }; this.store.setComponentClassForName(componentName.toString(), NavigationComponent); this.appRegistryService.registerComponent(componentName.toString(), NavigationComponent); diff --git a/lib/src/components/ComponentWrapper.test.tsx b/lib/src/components/ComponentWrapper.test.tsx index 4ebaf96092c..fdc333d601a 100644 --- a/lib/src/components/ComponentWrapper.test.tsx +++ b/lib/src/components/ComponentWrapper.test.tsx @@ -168,6 +168,13 @@ describe('ComponentWrapper', () => { expect(store.getComponentInstance('component1')).toBeTruthy(); }); + it('Component generator is invoked only once', () => { + const componentGenerator = jest.fn(() => MyComponent); + uut.wrap(componentName, componentGenerator, store, componentEventsObserver); + + expect(componentGenerator.mock.calls.length).toBe(1); + }); + describe(`register with redux store`, () => { class MyReduxComp extends React.Component { static options() { diff --git a/lib/src/components/ComponentWrapper.tsx b/lib/src/components/ComponentWrapper.tsx index be14c750633..ff8fd3f5ced 100644 --- a/lib/src/components/ComponentWrapper.tsx +++ b/lib/src/components/ComponentWrapper.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { ComponentProvider } from 'react-native'; -import merge from 'lodash/merge' +import merge from 'lodash/merge'; import { polyfill } from 'react-lifecycles-compat'; import hoistNonReactStatics from 'hoist-non-react-statics'; @@ -68,7 +68,7 @@ export class ComponentWrapper { } polyfill(WrappedComponent); - hoistNonReactStatics(WrappedComponent, concreteComponentProvider()); + hoistNonReactStatics(WrappedComponent, concreteComponentProvider === OriginalComponentGenerator ? GeneratedComponentClass : concreteComponentProvider()); return ReduxProvider ? this.wrapWithRedux(WrappedComponent, ReduxProvider, reduxStore) : WrappedComponent; } diff --git a/lib/src/components/Store.ts b/lib/src/components/Store.ts index 47700f7b314..884bfc8a2cc 100644 --- a/lib/src/components/Store.ts +++ b/lib/src/components/Store.ts @@ -1,3 +1,4 @@ +import * as React from 'react'; import { ComponentProvider } from 'react-native'; import { IWrappedComponent } from './ComponentWrapper'; @@ -5,6 +6,7 @@ export class Store { private componentsByName: Record = {}; private propsById: Record = {}; private componentsInstancesById: Record = {}; + private wrappedComponents: Record> = {}; updateProps(componentId: string, props: any) { this.propsById[componentId] = props; @@ -38,4 +40,16 @@ export class Store { getComponentInstance(id: string): IWrappedComponent { return this.componentsInstancesById[id]; } + + setWrappedComponent(componentName: string | number, wrappedComponent: React.ComponentClass): void { + this.wrappedComponents[componentName] = wrappedComponent; + } + + hasRegisteredWrappedComponent(componentName: string | number): boolean { + return componentName in this.wrappedComponents; + } + + getWrappedComponent(componentName: string | number): React.ComponentClass { + return this.wrappedComponents[componentName]; + } }