ReactNativeViewConfigRegistry.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @format
  8. * @flow strict-local
  9. */
  10. /* eslint-disable react-internal/invariant-args */
  11. 'use strict';
  12. import type {
  13. ReactNativeBaseComponentViewConfig,
  14. ViewConfigGetter,
  15. } from './ReactNativeTypes';
  16. const invariant = require('invariant');
  17. // Event configs
  18. const customBubblingEventTypes: {
  19. [eventName: string]: $ReadOnly<{|
  20. phasedRegistrationNames: $ReadOnly<{|
  21. captured: string,
  22. bubbled: string,
  23. |}>,
  24. |}>,
  25. ...,
  26. } = {};
  27. const customDirectEventTypes: {
  28. [eventName: string]: $ReadOnly<{|
  29. registrationName: string,
  30. |}>,
  31. ...,
  32. } = {};
  33. exports.customBubblingEventTypes = customBubblingEventTypes;
  34. exports.customDirectEventTypes = customDirectEventTypes;
  35. const viewConfigCallbacks = new Map();
  36. const viewConfigs = new Map();
  37. function processEventTypes(
  38. viewConfig: ReactNativeBaseComponentViewConfig<>,
  39. ): void {
  40. const {bubblingEventTypes, directEventTypes} = viewConfig;
  41. if (__DEV__) {
  42. if (bubblingEventTypes != null && directEventTypes != null) {
  43. for (const topLevelType in directEventTypes) {
  44. invariant(
  45. bubblingEventTypes[topLevelType] == null,
  46. 'Event cannot be both direct and bubbling: %s',
  47. topLevelType,
  48. );
  49. }
  50. }
  51. }
  52. if (bubblingEventTypes != null) {
  53. for (const topLevelType in bubblingEventTypes) {
  54. if (customBubblingEventTypes[topLevelType] == null) {
  55. customBubblingEventTypes[topLevelType] =
  56. bubblingEventTypes[topLevelType];
  57. }
  58. }
  59. }
  60. if (directEventTypes != null) {
  61. for (const topLevelType in directEventTypes) {
  62. if (customDirectEventTypes[topLevelType] == null) {
  63. customDirectEventTypes[topLevelType] = directEventTypes[topLevelType];
  64. }
  65. }
  66. }
  67. }
  68. /**
  69. * Registers a native view/component by name.
  70. * A callback is provided to load the view config from UIManager.
  71. * The callback is deferred until the view is actually rendered.
  72. */
  73. exports.register = function(name: string, callback: ViewConfigGetter): string {
  74. invariant(
  75. !viewConfigCallbacks.has(name),
  76. 'Tried to register two views with the same name %s',
  77. name,
  78. );
  79. invariant(
  80. typeof callback === 'function',
  81. 'View config getter callback for component `%s` must be a function (received `%s`)',
  82. name,
  83. callback === null ? 'null' : typeof callback,
  84. );
  85. viewConfigCallbacks.set(name, callback);
  86. return name;
  87. };
  88. /**
  89. * Retrieves a config for the specified view.
  90. * If this is the first time the view has been used,
  91. * This configuration will be lazy-loaded from UIManager.
  92. */
  93. exports.get = function(name: string): ReactNativeBaseComponentViewConfig<> {
  94. let viewConfig;
  95. if (!viewConfigs.has(name)) {
  96. const callback = viewConfigCallbacks.get(name);
  97. if (typeof callback !== 'function') {
  98. invariant(
  99. false,
  100. 'View config getter callback for component `%s` must be a function (received `%s`).%s',
  101. name,
  102. callback === null ? 'null' : typeof callback,
  103. typeof name[0] === 'string' && /[a-z]/.test(name[0])
  104. ? ' Make sure to start component names with a capital letter.'
  105. : '',
  106. );
  107. }
  108. viewConfig = callback();
  109. processEventTypes(viewConfig);
  110. viewConfigs.set(name, viewConfig);
  111. // Clear the callback after the config is set so that
  112. // we don't mask any errors during registration.
  113. viewConfigCallbacks.set(name, null);
  114. } else {
  115. viewConfig = viewConfigs.get(name);
  116. }
  117. invariant(viewConfig, 'View config not found for name %s', name);
  118. return viewConfig;
  119. };