mirror of
https://github.com/status-im/react-native.git
synced 2025-01-15 03:56:03 +00:00
0c9b41f2c0
Summary: cc hramos Create React Native App was designed to reduce "time to hello world" to 5-10 minutes for React Native apps. This PR would make CRNA the first way to get started that new users encounter. Included also is some text to help advanced users navigate the question of whether to use CRNA or whether to go straight to `react-native init`. It also includes a new banner for the iOS and Android guides, since they do not apply to CRNA users. Changes are only to the website, screenshots below. This branch was created from the last CI-passing master commit this morning, dependencies were freshly installed and these screenshots are from a clean build. [The Getting Started page](https://www.dropbox.com/s/1s7f3wu3oxr6gpo/Screenshot%202017-04-04%2015.12.29.png?dl=0) [The "native builds only" banner](https://www.dropbox.com/s/nyv51xdiibdkn57/Screenshot%202017-04-04%2015.13.25.png?dl=0) [Pages which still apply to CRNApps have no banner](https://www.dropbox.com/s/qgl0h6uzynqkmy2/Screenshot%202017-04-04%2015.14.10.png?dl=0) <details> * [x] Decide how to handle native code & react-native-cli references outside of the `banner: ejected` guides * [x] [Debugging: Accessing Console Logs](https://facebook.github.io/react-native/docs/debugging.html#accessing-console-logs) isn't needed in CRNA (logs are forwarded alongside packager output) * [x] [Debugging: With Stetho](https://facebook.github.io/react-native/docs/debugging.html#debugging-with-stetho-http-facebook-github-io-stetho-on-android) requires native code * [x] [Debugging: Debugging Native Code](https://facebook.github.io/react-native/docs/debugging.html#debugging-native-code) is native-only * [x] [AppRegistry](https://facebook.github.io/react-native/docs/appregistry.html) only applies to ejected apps, since this is generated from code, I don't think we can set `banner: ejected`? * [x] [Linking](https://facebook.github.io/react-native/docs/linking.html) involves changing Android manifests and other native-side things * [x] [PermissionsAndroid](https://facebook.github.io/react-native/docs/permissionsandroid.html) may be flaky in the Expo client, I can't recall (cc jesseruder) * [x] [PushNotificationIOS](https://facebook.github.io/react-native/docs/pushnotificationios.html) won't work inside Expo, as it has to [handle its own push notifs](https://docs.expo.io/versions/v15.0.0/guides/push-notifications.html) * [x] [Geolocation](https://facebook.github.io/react-native/docs/geolocation.html) requires a polyfill that will most likely ship with next week's release, but that won't have any manifest changes necessary * [ ] Figure out a strategy to handle the fact that CRNA will lag stable RN releases by ~1 week * [x] Confirm linking out to CRNA docs is an OK strategy as opposed to moving ejecting, etc. docs in-tree * [ ] Answer questions (I'll add some review comments to call out a few things) </details> Closes https://github.com/facebook/react-native/pull/13303 Differential Revision: D4950661 Pulled By: hramos fbshipit-source-id: 3dd43828f38ca6ede3f2b0683608c56420dc6525
245 lines
8.0 KiB
JavaScript
245 lines
8.0 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule AppRegistry
|
|
* @flow
|
|
*/
|
|
'use strict';
|
|
|
|
const BatchedBridge = require('BatchedBridge');
|
|
const BugReporting = require('BugReporting');
|
|
const FrameRateLogger = require('FrameRateLogger');
|
|
const NativeModules = require('NativeModules');
|
|
const ReactNative = require('ReactNative');
|
|
const SceneTracker = require('SceneTracker');
|
|
|
|
const infoLog = require('infoLog');
|
|
const invariant = require('fbjs/lib/invariant');
|
|
const renderApplication = require('renderApplication');
|
|
|
|
if (__DEV__) {
|
|
// In order to use Cmd+P to record/dump perf data, we need to make sure
|
|
// this module is available in the bundle
|
|
require('RCTRenderingPerf');
|
|
}
|
|
|
|
type Task = (taskData: any) => Promise<void>;
|
|
type TaskProvider = () => Task;
|
|
export type ComponentProvider = () => ReactClass<any>;
|
|
export type ComponentProviderInstrumentationHook =
|
|
(component: ComponentProvider) => ReactClass<any>;
|
|
export type AppConfig = {
|
|
appKey: string,
|
|
component?: ComponentProvider,
|
|
run?: Function,
|
|
section?: boolean,
|
|
};
|
|
export type Runnable = {
|
|
component?: ComponentProvider,
|
|
run: Function,
|
|
};
|
|
export type Runnables = {
|
|
[appKey: string]: Runnable,
|
|
};
|
|
export type Registry = {
|
|
sections: Array<string>,
|
|
runnables: Runnables,
|
|
};
|
|
|
|
const runnables: Runnables = {};
|
|
let runCount = 1;
|
|
const sections: Runnables = {};
|
|
const tasks: Map<string, TaskProvider> = new Map();
|
|
let componentProviderInstrumentationHook: ComponentProviderInstrumentationHook =
|
|
(component: ComponentProvider) => component();
|
|
let _frameRateLoggerSceneListener = null;
|
|
|
|
|
|
/**
|
|
* <div class="banner-crna-ejected">
|
|
* <h3>Project with Native Code Required</h3>
|
|
* <p>
|
|
* This API only works in projects made with <code>react-native init</code>
|
|
* or in those made with Create React Native App which have since ejected. For
|
|
* more information about ejecting, please see
|
|
* the <a href="https://github.com/react-community/create-react-native-app/blob/master/EJECTING.md" target="_blank">guide</a> on
|
|
* the Create React Native App repository.
|
|
* </p>
|
|
* </div>
|
|
*
|
|
* `AppRegistry` is the JS entry point to running all React Native apps. App
|
|
* root components should register themselves with
|
|
* `AppRegistry.registerComponent`, then the native system can load the bundle
|
|
* for the app and then actually run the app when it's ready by invoking
|
|
* `AppRegistry.runApplication`.
|
|
*
|
|
* To "stop" an application when a view should be destroyed, call
|
|
* `AppRegistry.unmountApplicationComponentAtRootTag` with the tag that was
|
|
* passed into `runApplication`. These should always be used as a pair.
|
|
*
|
|
* `AppRegistry` should be `require`d early in the `require` sequence to make
|
|
* sure the JS execution environment is setup before other modules are
|
|
* `require`d.
|
|
*/
|
|
const AppRegistry = {
|
|
registerConfig(config: Array<AppConfig>): void {
|
|
config.forEach((appConfig) => {
|
|
if (appConfig.run) {
|
|
AppRegistry.registerRunnable(appConfig.appKey, appConfig.run);
|
|
} else {
|
|
invariant(
|
|
appConfig.component != null,
|
|
'AppRegistry.registerConfig(...): Every config is expected to set ' +
|
|
'either `run` or `component`, but `%s` has neither.',
|
|
appConfig.appKey
|
|
);
|
|
AppRegistry.registerComponent(
|
|
appConfig.appKey,
|
|
appConfig.component,
|
|
appConfig.section,
|
|
);
|
|
}
|
|
});
|
|
},
|
|
|
|
registerComponent(
|
|
appKey: string,
|
|
component: ComponentProvider,
|
|
section?: boolean,
|
|
): string {
|
|
runnables[appKey] = {
|
|
component,
|
|
run: (appParameters) =>
|
|
renderApplication(
|
|
componentProviderInstrumentationHook(component),
|
|
appParameters.initialProps,
|
|
appParameters.rootTag
|
|
)
|
|
};
|
|
if (section) {
|
|
sections[appKey] = runnables[appKey];
|
|
}
|
|
return appKey;
|
|
},
|
|
|
|
registerRunnable(appKey: string, run: Function): string {
|
|
runnables[appKey] = {run};
|
|
return appKey;
|
|
},
|
|
|
|
registerSection(appKey: string, component: ComponentProvider): void {
|
|
AppRegistry.registerComponent(appKey, component, true);
|
|
},
|
|
|
|
getAppKeys(): Array<string> {
|
|
return Object.keys(runnables);
|
|
},
|
|
|
|
getSectionKeys(): Array<string> {
|
|
return Object.keys(sections);
|
|
},
|
|
|
|
getSections(): Runnables {
|
|
return {
|
|
...sections
|
|
};
|
|
},
|
|
|
|
getRunnable(appKey: string): ?Runnable {
|
|
return runnables[appKey];
|
|
},
|
|
|
|
getRegistry(): Registry {
|
|
return {
|
|
sections: AppRegistry.getSectionKeys(),
|
|
runnables: {...runnables},
|
|
};
|
|
},
|
|
|
|
setComponentProviderInstrumentationHook(hook: ComponentProviderInstrumentationHook) {
|
|
componentProviderInstrumentationHook = hook;
|
|
},
|
|
|
|
runApplication(appKey: string, appParameters: any): void {
|
|
const msg =
|
|
'Running application "' + appKey + '" with appParams: ' +
|
|
JSON.stringify(appParameters) + '. ' +
|
|
'__DEV__ === ' + String(__DEV__) +
|
|
', development-level warning are ' + (__DEV__ ? 'ON' : 'OFF') +
|
|
', performance optimizations are ' + (__DEV__ ? 'OFF' : 'ON');
|
|
infoLog(msg);
|
|
BugReporting.addSource('AppRegistry.runApplication' + runCount++, () => msg);
|
|
invariant(
|
|
runnables[appKey] && runnables[appKey].run,
|
|
'Application ' + appKey + ' has not been registered.\n\n' +
|
|
'Hint: This error often happens when you\'re running the packager ' +
|
|
'(local dev server) from a wrong folder. For example you have ' +
|
|
'multiple apps and the packager is still running for the app you ' +
|
|
'were working on before.\nIf this is the case, simply kill the old ' +
|
|
'packager instance (e.g. close the packager terminal window) ' +
|
|
'and start the packager in the correct app folder (e.g. cd into app ' +
|
|
'folder and run \'npm start\').\n\n' +
|
|
'This error can also happen due to a require() error during ' +
|
|
'initialization or failure to call AppRegistry.registerComponent.\n\n'
|
|
);
|
|
if (!_frameRateLoggerSceneListener) {
|
|
_frameRateLoggerSceneListener = SceneTracker.addActiveSceneChangedListener(
|
|
(scene) => FrameRateLogger.setContext(scene.name)
|
|
);
|
|
}
|
|
SceneTracker.setActiveScene({name: appKey});
|
|
runnables[appKey].run(appParameters);
|
|
},
|
|
|
|
unmountApplicationComponentAtRootTag(rootTag: number): void {
|
|
ReactNative.unmountComponentAtNodeAndRemoveContainer(rootTag);
|
|
},
|
|
|
|
/**
|
|
* Register a headless task. A headless task is a bit of code that runs without a UI.
|
|
* @param taskKey the key associated with this task
|
|
* @param task a promise returning function that takes some data passed from the native side as
|
|
* the only argument; when the promise is resolved or rejected the native side is
|
|
* notified of this event and it may decide to destroy the JS context.
|
|
*/
|
|
registerHeadlessTask(taskKey: string, task: TaskProvider): void {
|
|
if (tasks.has(taskKey)) {
|
|
console.warn(`registerHeadlessTask called multiple times for same key '${taskKey}'`);
|
|
}
|
|
tasks.set(taskKey, task);
|
|
},
|
|
|
|
/**
|
|
* Only called from native code. Starts a headless task.
|
|
*
|
|
* @param taskId the native id for this task instance to keep track of its execution
|
|
* @param taskKey the key for the task to start
|
|
* @param data the data to pass to the task
|
|
*/
|
|
startHeadlessTask(taskId: number, taskKey: string, data: any): void {
|
|
const taskProvider = tasks.get(taskKey);
|
|
if (!taskProvider) {
|
|
throw new Error(`No task registered for key ${taskKey}`);
|
|
}
|
|
taskProvider()(data)
|
|
.then(() => NativeModules.HeadlessJsTaskSupport.notifyTaskFinished(taskId))
|
|
.catch(reason => {
|
|
console.error(reason);
|
|
NativeModules.HeadlessJsTaskSupport.notifyTaskFinished(taskId);
|
|
});
|
|
}
|
|
|
|
};
|
|
|
|
BatchedBridge.registerCallableModule(
|
|
'AppRegistry',
|
|
AppRegistry
|
|
);
|
|
|
|
module.exports = AppRegistry;
|