Move HelloWorld template to a single index.js entry point

Summary:
This change (initially discussed in https://github.com/react-community/create-react-native-app/issues/26) moves the HelloWorld project template from two nearly identical entry points (`index.android.js` and `index.ios.js`) to a single, minimal `index.js` entry point. The root component is created in `App.js`. This unifies the project structure between `react-native init` and Create React Native App and allows CRNA's eject to use the entry point from the HelloWorld template without any hacks to customize it. Also examples in the docs can be just copy-pasted to `App.js` the same way in both HelloWorld and CRNA apps without having to first learn about  `AppRegistry.registerComponent`.

* Created a new project from the template using `./scripts/test-manual-e2e.sh` and verified that:
  * The app builds, starts and runs both on Android and iOS.
  * Editing and reloading changes works.
  * The new files (`index.js`, `App.js`, `__tests__/App.js`) get created in the project folder.

<img width="559" alt="screen shot 2017-08-01 at 19 10 51" src="https://user-images.githubusercontent.com/497214/28835171-300a12b6-76ed-11e7-81b2-623639c3b8f6.png">
<img width="467" alt="screen shot 2017-08-01 at 19 09 12" src="https://user-images.githubusercontent.com/497214/28835180-33d285e0-76ed-11e7-8d68-2b3bc44bf585.png">

<!--
Thank you for sending the PR!

If you changed any code, please provide us with clear instructions on how you verified your changes work. In other words, a test plan is *required*. Bonus points for screenshots and videos!

Please read the Contribution Guidelines at https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md to learn more about contributing to React Native.

Happy contributing!
-->
Closes https://github.com/facebook/react-native/pull/15312

Differential Revision: D5556276

Pulled By: hramos

fbshipit-source-id: 068fdf7e51381c2bc50321522f2be0db47296c5e
This commit is contained in:
Ville Immonen 2017-08-03 13:52:06 -07:00 committed by Facebook Github Bot
parent f5f5ed5ba6
commit 6e99e314b2
20 changed files with 71 additions and 134 deletions

View File

@ -230,13 +230,13 @@ function e2e_suite() {
# js tests
if [ $RUN_JS -ne 0 ]; then
# Check the packager produces a bundle (doesn't throw an error)
react-native bundle --max-workers 1 --platform android --dev true --entry-file index.android.js --bundle-output android-bundle.js
react-native bundle --max-workers 1 --platform android --dev true --entry-file index.js --bundle-output android-bundle.js
if [ $? -ne 0 ]; then
echo "Could not build android bundle"
return 1
fi
react-native bundle --max-workers 1 --platform ios --dev true --entry-file index.ios.js --bundle-output ios-bundle.js
react-native bundle --max-workers 1 --platform ios --dev true --entry-file index.js --bundle-output ios-bundle.js
if [ $? -ne 0 ]; then
echo "Could not build iOS bundle"
return 1

View File

@ -583,12 +583,12 @@ Now that you have successfully run the app, let's modify it.
<block class="native mac ios" />
- Open `index.ios.js` in your text editor of choice and edit some lines.
- Open `index.js` in your text editor of choice and edit some lines.
- Hit `⌘R` in your iOS Simulator to reload the app and see your changes!
<block class="native mac android" />
- Open `index.android.js` in your text editor of choice and edit some lines.
- Open `index.js` in your text editor of choice and edit some lines.
- Press the `R` key twice or select `Reload` from the Developer Menu (`⌘M`) to see your changes!
<block class="native windows linux android" />
@ -597,7 +597,7 @@ Now that you have successfully run the app, let's modify it.
Now that you have successfully run the app, let's modify it.
- Open `index.android.js` in your text editor of choice and edit some lines.
- Open `index.js` in your text editor of choice and edit some lines.
- Press the `R` key twice or select `Reload` from the Developer Menu (`⌘M`) to see your changes!
<block class="native mac ios android" />

View File

@ -292,15 +292,15 @@ Now we will actually modify the native iOS application to integrate React Native
The first bit of code we will write is the actual React Native code for the new "High Score" screen that will be integrated into our application.
##### 1. Create a `index.ios.js` file
##### 1. Create a `index.js` file
First, create an empty `index.ios.js` file in the root of your React Native project.
First, create an empty `index.js` file in the root of your React Native project.
`index.ios.js` is the starting point for React Native applications on iOS, and it is always required. It can be a small file that `require`s other file that are part of your React Native component or application, or it can contain all the code that is needed for it. In our case, we will just put everything in `index.ios.js`.
`index.js` is the starting point for React Native applications, and it is always required. It can be a small file that `require`s other file that are part of your React Native component or application, or it can contain all the code that is needed for it. In our case, we will just put everything in `index.js`.
##### 2. Add your React Native code
In your `index.ios.js`, create your component. In our sample here, we will add simple `<Text>` component within a styled `<View>`
In your `index.js`, create your component. In our sample here, we will add simple `<Text>` component within a styled `<View>`
```javascript
'use strict';
@ -358,7 +358,7 @@ AppRegistry.registerComponent('MyReactNativeApp', () => RNHighScores);
#### The Magic: `RCTRootView`
Now that your React Native component is created via `index.ios.js`, you need to add that component to a new or existing `ViewController`. The easiest path to take is to optionally create an event path to your component and then add that component to an existing `ViewController`.
Now that your React Native component is created via `index.js`, you need to add that component to a new or existing `ViewController`. The easiest path to take is to optionally create an event path to your component and then add that component to an existing `ViewController`.
We will tie our React Native component with a new native view in the `ViewController` that will actually host it called `RCTRootView` .
@ -372,9 +372,9 @@ You can add a new link on the main game menu to go to the "High Score" React Nat
We will now add an event handler from the menu link. A method will be added to the main `ViewController` of your application. This is where `RCTRootView` comes into play.
When you build a React Native application, you use the React Native packager to create an `index.ios.bundle` that will be served by the React Native server. Inside `index.ios.bundle` will be our `RNHighScore` module. So, we need to point our `RCTRootView` to the location of the `index.ios.bundle` resource (via `NSURL`) and tie it to the module.
When you build a React Native application, you use the React Native packager to create an `index.bundle` that will be served by the React Native server. Inside `index.bundle` will be our `RNHighScore` module. So, we need to point our `RCTRootView` to the location of the `index.bundle` resource (via `NSURL`) and tie it to the module.
We will, for debugging purposes, log that the event handler was invoked. Then, we will create a string with the location of our React Native code that exists inside the `index.ios.bundle`. Finally, we will create the main `RCTRootView`. Notice how we provide `RNHighScores` as the `moduleName` that we created [above](#the-react-native-component) when writing the code for our React Native component.
We will, for debugging purposes, log that the event handler was invoked. Then, we will create a string with the location of our React Native code that exists inside the `index.bundle`. Finally, we will create the main `RCTRootView`. Notice how we provide `RNHighScores` as the `moduleName` that we created [above](#the-react-native-component) when writing the code for our React Native component.
<block class="objc" />
@ -389,7 +389,7 @@ First `import` the `RCTRootView` header.
```objectivec
- (IBAction)highScoreButtonPressed:(id)sender {
NSLog(@"High Score Button Pressed");
NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.bundle?platform=ios"];
RCTRootView *rootView =
[[RCTRootView alloc] initWithBundleURL: jsCodeLocation
@ -429,7 +429,7 @@ import React
```swift
@IBAction func highScoreButtonTapped(sender : UIButton) {
NSLog("Hello")
let jsCodeLocation = URL(string: "http://localhost:8081/index.ios.bundle?platform=ios")
let jsCodeLocation = URL(string: "http://localhost:8081/index.bundle?platform=ios")
let mockData:NSDictionary = ["scores":
[
["name":"Alex", "value":"42"],
@ -471,7 +471,7 @@ Wire up the new link in the main menu to the newly added event handler method.
### Test your integration
You have now done all the basic steps to integrate React Native with your current application. Now we will start the React Native packager to build the `index.ios.bundle` package and the server running on `localhost` to serve it.
You have now done all the basic steps to integrate React Native with your current application. Now we will start the React Native packager to build the `index.bundle` package and the server running on `localhost` to serve it.
##### 1. Add App Transport Security exception
@ -586,15 +586,15 @@ Now we will actually modify the native Android application to integrate React Na
The first bit of code we will write is the actual React Native code for the new "High Score" screen that will be integrated into our application.
##### 1. Create a `index.android.js` file
##### 1. Create a `index.js` file
First, create an empty `index.android.js` file in the root of your React Native project.
First, create an empty `index.js` file in the root of your React Native project.
`index.android.js` is the starting point for React Native applications on Android, and it is always required. It can be a small file that `require`s other file that are part of your React Native component or application, or it can contain all the code that is needed for it. In our case, we will just put everything in `index.android.js`.
`index.js` is the starting point for React Native applications, and it is always required. It can be a small file that `require`s other file that are part of your React Native component or application, or it can contain all the code that is needed for it. In our case, we will just put everything in `index.js`.
##### 2. Add your React Native code
In your `index.android.js`, create your component. In our sample here, we will add simple `<Text>` component within a styled `<View>`:
In your `index.js`, create your component. In our sample here, we will add simple `<Text>` component within a styled `<View>`:
```javascript
'use strict';
@ -679,7 +679,7 @@ public class MyReactActivity extends Activity implements DefaultHardwareBackBtnH
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")
.setJSMainModuleName("index")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
@ -696,7 +696,7 @@ public class MyReactActivity extends Activity implements DefaultHardwareBackBtnH
}
```
> If you are using a starter kit for React Native, replace the "HelloWorld" string with the one in your index.android.js file (its the first argument to the `AppRegistry.registerComponent()` method).
> If you are using a starter kit for React Native, replace the "HelloWorld" string with the one in your index.js file (its the first argument to the `AppRegistry.registerComponent()` method).
If you are using Android Studio, use `Alt + Enter` to add all missing imports in your MyReactActivity class. Be careful to use your packages `BuildConfig` and not the one from the `...facebook...` package.
@ -775,7 +775,7 @@ Now your activity is ready to run some JavaScript code.
### Test your integration
You have now done all the basic steps to integrate React Native with your current application. Now we will start the React Native packager to build the `index.android.bundle` package and the server running on localhost to serve it.
You have now done all the basic steps to integrate React Native with your current application. Now we will start the React Native packager to build the `index.bundle` package and the server running on localhost to serve it.
##### 1. Run the packager
@ -798,7 +798,7 @@ Once you reach your React-powered activity inside the app, it should load the Ja
You can use Android Studio to create your release builds too! Its as easy as creating release builds of your previously-existing native Android app. Theres just one additional step, which youll have to do before every release build. You need to execute the following to create a React Native bundle, which will be included with your native Android app:
```
$ react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output android/com/your-company-name/app-package-name/src/main/assets/index.android.bundle --assets-dest android/com/your-company-name/app-package-name/src/main/res/
$ react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/com/your-company-name/app-package-name/src/main/assets/index.android.bundle --assets-dest android/com/your-company-name/app-package-name/src/main/res/
```
> Dont forget to replace the paths with correct ones and create the assets folder if it doesnt exist.

View File

@ -19,7 +19,7 @@ In accordance with the ancient traditions of our people, we must first build an
```ReactNativeWebPlayer
import React, { Component } from 'react';
import { AppRegistry, Text } from 'react-native';
import { Text } from 'react-native';
export default class HelloWorldApp extends Component {
render() {
@ -28,12 +28,9 @@ export default class HelloWorldApp extends Component {
);
}
}
// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => HelloWorldApp);
```
If you are feeling curious, you can play around with sample code directly in the web simulators. You can also paste it into your `App.js`, `index.ios.js`, or `index.android.js` file to create a real app on your local machine.
If you are feeling curious, you can play around with sample code directly in the web simulators. You can also paste it into your `App.js` file to create a real app on your local machine.
## What's going on here?
@ -48,14 +45,6 @@ is a built-in component that just displays some text.
So this code is defining `HelloWorldApp`, a new `Component`. When you're building a React Native app, you'll be making new components a lot. Anything you see on the screen is some sort of component. A component can be pretty simple - the only thing that's required is a `render` function which returns some JSX to render.
<div class="banner-crna-ejected">
<h3>Projects With Native Code Only</h3>
<p>
In the particular example above, <code>HelloWorldApp</code> is registered with the <code>AppRegistry</code>. The <code>AppRegistry</code> just tells React Native which component is the root one for the whole application. It's included in these examples so you can paste the whole thing into your <code>index.ios.js</code> or <code>index.android.js</code> file and get it running. If you have a project from Create React Native App, this is handled for you and it's not necessary to call <code>AppRegistry</code> in your code.
</p>
</div>
## This app doesn't do very much
Good point. To make components do more interesting things, you need to [learn about Props](docs/props.html).

View File

@ -44,6 +44,8 @@ function copyProjectTemplateAndReplace(srcPath, destPath, newProjectName, option
// This also includes __tests__/index.*.js
if (fileName === 'index.ios.js') { return; }
if (fileName === 'index.android.js') { return; }
if (fileName === 'index.js') { return; }
if (fileName === 'App.js') { return; }
}
const relativeFilePath = path.relative(srcPath, absoluteSrcFilePath);

View File

@ -9,13 +9,13 @@
import React, { Component } from 'react';
import { StackNavigator } from 'react-navigation';
import HomeScreenTabNavigator from './HomeScreenTabNavigator';
import ChatScreen from './chat/ChatScreen';
import HomeScreenTabNavigator from './views/HomeScreenTabNavigator';
import ChatScreen from './views/chat/ChatScreen';
/**
* Top-level navigator. Renders the application UI.
*/
const MainNavigator = StackNavigator({
const App = StackNavigator({
Home: {
screen: HomeScreenTabNavigator,
},
@ -24,4 +24,4 @@ const MainNavigator = StackNavigator({
},
});
export default MainNavigator;
export default App;

View File

@ -1,5 +0,0 @@
import { AppRegistry } from 'react-native';
import MainNavigator from './views/MainNavigator';
AppRegistry.registerComponent('HelloWorld', () => MainNavigator);

View File

@ -1,5 +0,0 @@
import { AppRegistry } from 'react-native';
import MainNavigator from './views/MainNavigator';
AppRegistry.registerComponent('HelloWorld', () => MainNavigator);

View File

@ -6,13 +6,20 @@
import React, { Component } from 'react';
import {
AppRegistry,
Platform,
StyleSheet,
Text,
View
} from 'react-native';
export default class HelloWorld extends Component {
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' +
'Cmd+D or shake for dev menu',
android: 'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
export default class App extends Component {
render() {
return (
<View style={styles.container}>
@ -20,11 +27,10 @@ export default class HelloWorld extends Component {
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.android.js
To get started, edit App.js
</Text>
<Text style={styles.instructions}>
Double tap R on your keyboard to reload,{'\n'}
Shake or press menu button for dev menu
{instructions}
</Text>
</View>
);
@ -49,5 +55,3 @@ const styles = StyleSheet.create({
marginBottom: 5,
},
});
AppRegistry.registerComponent('HelloWorld', () => HelloWorld);

View File

@ -1,12 +1,12 @@
import 'react-native';
import React from 'react';
import Index from '../index.ios.js';
import App from '../App';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('renders correctly', () => {
const tree = renderer.create(
<Index />
<App />
);
});

View File

@ -1,12 +0,0 @@
import 'react-native';
import React from 'react';
import Index from '../index.android.js';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('renders correctly', () => {
const tree = renderer.create(
<Index />
);
});

View File

@ -72,6 +72,10 @@ import com.android.build.OutputFile
* ]
*/
project.ext.react = [
entryFile: "index.js"
]
apply from: "../../node_modules/react-native/react.gradle"
/**

View File

@ -25,6 +25,11 @@ public class MainApplication extends Application implements ReactApplication {
new MainReactPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override

View File

@ -1,53 +0,0 @@
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
export default class HelloWorld extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.ios.js
</Text>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('HelloWorld', () => HelloWorld);

View File

@ -0,0 +1,4 @@
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('HelloWorld', () => App);

View File

@ -18,7 +18,7 @@
{
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"HelloWorld"

View File

@ -97,7 +97,7 @@ describe('Android Test App', function () {
});
it('should have Hot Module Reloading working', function () {
const androidAppCode = fs.readFileSync('index.android.js', 'utf-8');
const androidAppCode = fs.readFileSync('index.js', 'utf-8');
let intervalToUpdate;
return driver
.waitForElementByXPath('//android.widget.TextView[starts-with(@text, "Welcome to React Native!")]')
@ -111,19 +111,19 @@ describe('Android Test App', function () {
// CI environment can be quite slow and we can't guarantee that it can consistently motice a file change
// so we change the file every few seconds just in case
intervalToUpdate = setInterval(() => {
fs.writeFileSync('index.android.js', androidAppCode.replace('Welcome to React Native!', 'Welcome to React Native with HMR!' + iteration), 'utf-8');
fs.writeFileSync('index.js', androidAppCode.replace('Welcome to React Native!', 'Welcome to React Native with HMR!' + iteration), 'utf-8');
}, 3000);
})
.waitForElementByXPath('//android.widget.TextView[starts-with(@text, "Welcome to React Native with HMR!")]')
.finally(() => {
clearInterval(intervalToUpdate);
fs.writeFileSync('index.android.js', androidAppCode, 'utf-8');
fs.writeFileSync('index.js', androidAppCode, 'utf-8');
});
});
it('should have Debug In Chrome working', function () {
const androidAppCode = fs.readFileSync('index.android.js', 'utf-8');
const androidAppCode = fs.readFileSync('index.js', 'utf-8');
// http://developer.android.com/reference/android/view/KeyEvent.html#KEYCODE_MENU
return driver
.waitForElementByXPath('//android.widget.TextView[starts-with(@text, "Welcome to React Native!")]')

View File

@ -49,7 +49,11 @@ cd "${REACT_NATIVE_DIR}"/../..
[ -z "$NVM_DIR" ] && export NVM_DIR="$HOME/.nvm"
# Define entry file
ENTRY_FILE=${1:-index.ios.js}
if [[ -s "index.ios.js" ]]; then
ENTRY_FILE=${1:-index.ios.js}
else
ENTRY_FILE=${1:-index.js}
fi
if [[ -s "$HOME/.nvm/nvm.sh" ]]; then
. "$HOME/.nvm/nvm.sh"

View File

@ -167,7 +167,7 @@ try {
SERVER_PID = packagerProcess.pid;
exec('sleep 15s');
// prepare cache to reduce chances of possible red screen "Can't fibd variable __fbBatchedBridge..."
exec('response=$(curl --write-out %{http_code} --silent --output /dev/null localhost:8081/index.ios.bundle?platform=ios&dev=true)');
exec('response=$(curl --write-out %{http_code} --silent --output /dev/null localhost:8081/index.bundle?platform=ios&dev=true)');
echo(`Starting packager server, ${SERVER_PID}`);
echo('Executing ' + iosTestType + ' e2e test');
if (tryExecNTimes(
@ -190,12 +190,12 @@ try {
if (argv.js) {
// Check the packager produces a bundle (doesn't throw an error)
if (exec('react-native bundle --max-workers 1 --platform android --dev true --entry-file index.android.js --bundle-output android-bundle.js').code) {
if (exec('react-native bundle --max-workers 1 --platform android --dev true --entry-file index.js --bundle-output android-bundle.js').code) {
echo('Could not build Android bundle');
exitCode = 1;
throw Error(exitCode);
}
if (exec('react-native --max-workers 1 bundle --platform ios --dev true --entry-file index.ios.js --bundle-output ios-bundle.js').code) {
if (exec('react-native --max-workers 1 bundle --platform ios --dev true --entry-file index.js --bundle-output ios-bundle.js').code) {
echo('Could not build iOS bundle');
exitCode = 1;
throw Error(exitCode);

View File

@ -98,7 +98,7 @@ info " - Disable Hot Reloading. It might be enabled from last time (the settin
info " - Verify 'Reload JS' works"
info " - Test Chrome debugger by adding breakpoints and reloading JS. We don't have tests for Chrome debugging."
info " - Disable Chrome debugging."
info " - Enable Hot Reloading, change a file (index.ios.js, index.android.js) and save. The UI should refresh."
info " - Enable Hot Reloading, change a file (index.js) and save. The UI should refresh."
info " - Disable Hot Reloading."
info ""
info "Press any key to open the project in Xcode"