RN: Create `NativeComponentRegistry`

Summary:
Creates `NativeComponentRegistry` which makes native component initialization more declarative and configurable through an optionally configurable provider.

The next diff converts `ScrollView` to use this new abstraction as a demonstration. The plan would be to use this to replace all current manual call sites of `registerGeneratedViewConfig`, and then the ones generated via the Babel plugin.

Migrating to this will not change any production behavior, but it will enable verification of `ViewConfig` in development.

Changelog:
[Internal]

Reviewed By: JoshuaGross

Differential Revision: D25084468

fbshipit-source-id: 9c758ddc279bf937a401a868a066907a94098f37
This commit is contained in:
Tim Yung 2020-11-19 10:53:49 -08:00 коммит произвёл Facebook GitHub Bot
Родитель c797fcf5aa
Коммит 6a547c6c57
3 изменённых файлов: 123 добавлений и 25 удалений

Просмотреть файл

@ -0,0 +1,78 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
'use strict';
import {createViewConfig} from './ViewConfig';
import type {
HostComponent,
PartialViewConfig,
} from '../Renderer/shims/ReactNativeTypes';
import ReactNativeViewConfigRegistry from '../Renderer/shims/ReactNativeViewConfigRegistry';
import getNativeComponentAttributes from '../ReactNative/getNativeComponentAttributes';
import verifyComponentAttributeEquivalence from '../Utilities/verifyComponentAttributeEquivalence';
import invariant from 'invariant';
let getRuntimeConfig;
/**
* Configures a function that is called to determine whether a given component
* should be registered using reflection of the native component at runtime.
*/
export function setRuntimeConfigProvider(
runtimeConfigProvider: (name: string) => {native: boolean, verify: boolean},
): void {
invariant(
getRuntimeConfig == null,
'NativeComponentRegistry.setRuntimeConfigProvider() called more than once.',
);
getRuntimeConfig = runtimeConfigProvider;
}
/**
* Gets a `NativeComponent` that can be rendered by React Native.
*
* The supplied `viewConfigProvider` may or may not be invoked and utilized,
* depending on how `setRuntimeConfigProvider` is configured.
*/
export function get<Config>(
name: string,
viewConfigProvider: () => PartialViewConfig,
): HostComponent<Config> {
ReactNativeViewConfigRegistry.register(name, () => {
const {native, verify} = getRuntimeConfig?.(name) ?? {
native: true,
verify: false,
};
const viewConfig = native
? getNativeComponentAttributes(name)
: createViewConfig(viewConfigProvider());
if (verify) {
if (native) {
verifyComponentAttributeEquivalence(
viewConfig,
createViewConfig(viewConfigProvider()),
);
} else {
verifyComponentAttributeEquivalence(
getNativeComponentAttributes(name),
viewConfig,
);
}
}
return viewConfig;
});
// $FlowFixMe[incompatible-return] `NativeComponent` is actually string!
return name;
}

Просмотреть файл

@ -0,0 +1,36 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
const React = require('react');
module.exports = viewName => {
const Component = class extends React.Component {
render() {
return React.createElement(viewName, this.props, this.props.children);
}
// The methods that exist on host components
blur = jest.fn();
focus = jest.fn();
measure = jest.fn();
measureInWindow = jest.fn();
measureLayout = jest.fn();
setNativeProps = jest.fn();
};
if (viewName === 'RCTView') {
Component.displayName = 'View';
} else {
Component.displayName = viewName;
}
return Component;
};

Просмотреть файл

@ -325,33 +325,17 @@ jest
}),
},
}))
.mock('../Libraries/ReactNative/requireNativeComponent', () => {
const React = require('react');
return viewName => {
const Component = class extends React.Component {
render() {
return React.createElement(viewName, this.props, this.props.children);
}
// The methods that exist on host components
blur = jest.fn();
focus = jest.fn();
measure = jest.fn();
measureInWindow = jest.fn();
measureLayout = jest.fn();
setNativeProps = jest.fn();
};
if (viewName === 'RCTView') {
Component.displayName = 'View';
} else {
Component.displayName = viewName;
}
return Component;
.mock('../Libraries/NativeComponent/NativeComponentRegistry', () => {
return {
get: jest.fn((name, viewConfigProvider) => {
return jest.requireActual('./mockNativeComponent')(name);
}),
setRuntimeConfigProvider: jest.fn(),
};
})
.mock('../Libraries/ReactNative/requireNativeComponent', () => {
return jest.requireActual('./mockNativeComponent');
})
.mock(
'../Libraries/Utilities/verifyComponentAttributeEquivalence',
() => function() {},