Compare commits

...
Author SHA1 Message Date
James Ide 113ebb3254 [0.42.3] Bump version numbers 2017-03-17 23:56:00 -07:00
Adam PerryandJames Ide 1a7e85e03e Eject CLI command to re-create native folders (#13009)
Summary:
The iOS and Android native folders of a React Native app can be difficult to maintain. This introduces a new workflow for creating and maintaining the native code of your app.

Now it will be possible to:

1. Remove the native iOS or Android folders
2. Create an `app.json` for your app, with at least a `name` and `displayName`
3. Run `react-native eject`, and the native code for your app will be generated

Then, as usual, you can run `react-native run-ios` and `react-native run-android`, to build and launch your app

For apps that don't have any native code, it will be possible to ignore the `ios` and `android` folders from version control.

Eject step tested in RN app by deleting native folders.

mkonicek, what is the best way to test `react-native init`?

As follow-up items, we can enable the following:

- Configuring app icon and launch screen from the `app.json`
- Automatically run `react-native link` for native libraries
- A
Closes https://github.com/facebook/react-native/pull/12162

Differential Revision: D4509138

Pulled By: ericvicenti

fbshipit-source-id: 0ee213e68f0a3d44bfce337e3ec43e5024bacc66
2017-03-17 23:54:12 -07:00
Mike Grabowski d2c63e4998 [0.42.2] Bump version numbers 2017-03-17 12:31:35 +01:00
Valentin SherginandHector Ramos 5f01aa78de [-RCTUIManager setFrame:forView:] was replaced with [-RCTUIManager setSize:forView:]
Reviewed By: fkgozali

Differential Revision: D4511712

fbshipit-source-id: 5c51d573bf638cb9b55d5f03b9f1882dc6129510
2017-03-14 11:32:12 -07:00
Martin Konicek d873f52574 [0.42.1] Bump version numbers 2017-03-10 17:54:27 +00:00
cailenmusselmanandMartin Konicek f78ec778ce YellowBox" Only provide elevation style prop when running on Android
Summary:
For unknown reasons, setting elevation: Number.MAX_VALUE causes remote debugging to hang on iOS (some sort of overflow maybe). Setting it to Number.MAX_SAFE_INTEGER fixes the iOS issue, but since elevation is an android-only style property we might as well remove it altogether for iOS.

See: https://github.com/facebook/react-native/issues/12223
Closes https://github.com/facebook/react-native/pull/12744

Differential Revision: D4684524

Pulled By: mkonicek

fbshipit-source-id: 7fb4f6da1c5c0cb437beff0e75122523e7233b72
2017-03-10 17:51:01 +00:00
Martin KonicekandHector Ramos 6ebd6bca5a Website: Fix aspect ratio of icons in the showcase on homepage
Summary:
I ran the website locally and noticed the icons in the showcase on the homepage look squashed:

![screenshot 2017-03-07 22 28 23](https://cloud.githubusercontent.com/assets/346214/23681227/18fbec6e-0386-11e7-9658-93907cbb3337.png)

Made the icons consistent with /showcase.html:

https://github.com/facebook/react-native/blob/d54c7f82822f623a903858d8d686d7f14ec71aae/website/styles/_showcase.scss#L74
Closes https://github.com/facebook/react-native/pull/12773

Differential Revision: D4669767

Pulled By: hramos

fbshipit-source-id: be5a758dd912083fc02c8e0b56c30c8617dcf3df
2017-03-09 14:06:53 -08:00
Hector Ramos 1b7de4f676 Replace defaultLink with infoLink in Showcase
Reviewed By: ericvicenti

Differential Revision: D4671154

fbshipit-source-id: ee9df269b91ec140afc8dace9bbf960f9a47f0b0
2017-03-09 14:05:21 -08:00
Mario StalloneandHector Ramos 359dc58b33 Updating the info about Myntra
Summary:
Updating the info about Myntra
We've also gotten our Android apps on React Native now. I also gave a talk on our journey and have updated the infoLink for the same.

I currently work for Myntra.
Closes https://github.com/facebook/react-native/pull/12736

Differential Revision: D4659904

Pulled By: hramos

fbshipit-source-id: c2a93d34e56979c05106d378695dbc5345bf9a2c
2017-03-09 14:03:12 -08:00
Héctor RamosandHector Ramos dbe978a40a Update Showcase
Summary:
Doing some cleanup here, mostly removing entries that lack an engineering blog post focused on React Native.

- Removes links to F8 2016 app. New entry coming soon!
- Add [Tesla](https://twitter.com/timdorr/status/830423561592532993)!
- Update some article titles, and link to newer content
Closes https://github.com/facebook/react-native/pull/12693

Differential Revision: D4654541

Pulled By: hramos

fbshipit-source-id: 3b22309ecc344fb5de2bcab36a64b69dd55c2f78
2017-03-09 14:03:02 -08:00
Hector Ramos 04ef14f7d2 Replace defaultLink with infoLink in Showcase
Reviewed By: ericvicenti

Differential Revision: D4671154

fbshipit-source-id: ee9df269b91ec140afc8dace9bbf960f9a47f0b0
2017-03-09 14:00:19 -08:00
Spencer AhrensandHector Ramos ca099814ae preserve inline components in prop type doc gen
Summary:
Some of the operations, like `oneOf` and `arrayOf`, were doing joins on arrays of potential
tags, like `<span>` or `<a>`, which would stringify them to `[object Object]`.

This introduces a new `spanJoinMapper` which suppresses the trailing separator like `join` but wraps
elements in `<span>` rather than `concat`ing into a string.

Latest master is pretty broken:
{F66059444}

Nested `color` and some other props were broken even before https://github.com/facebook/react-native/commit/a7a3922b89d821b9a34d26bdcc7676e747a2716
{F66059446}

All fixed in this diff:
{F66059445}

Reviewed By: mkonicek

Differential Revision: D4670441

fbshipit-source-id: ddc10f13b3bdc6a1e799fa06a4e206f8dbd08769
2017-03-09 13:58:30 -08:00
Hector Ramos b224414262 Merge Android UI Performance into Performance doc, reorder sidebar
Summary:
Doing some cleanup in preparation for CRNA.
Recommend `FlatList` and React Navigation for perf.
Tag docs that may only apply to apps ejected from CRNA. Currently has no effect.
Closes https://github.com/facebook/react-native/pull/12692

Differential Revision: D4654077

Pulled By: hramos

fbshipit-source-id: 1245d80d66e37d9dca9e9daf23e8b93c65cd1bf7
2017-03-09 13:52:56 -08:00
Spencer AhrensandHector Ramos 6e936f34ef Improve doc generation with flow types
Reviewed By: vjeux, hramos

Differential Revision: D4638610

fbshipit-source-id: 122d6fb68d521b675019a0698b6427960bb2d070
2017-03-09 13:51:13 -08:00
Hector Ramos 4c162e25c9 Move showcase source data into a JSON file
Summary:
The showcase is displayed in the homepage as well as in a standalone showcase page. The source data for these two pages is embedded in each page, resulting in repetition of data across both.

This PR splits out all of the showcase data to its own file, which is then loaded into the Metadata component during website generation.

`cd website && npm start`, then verified index and showcase loaded successfully
`cd website && node server/generate.js`, confirmed script runs successfully
Closes https://github.com/facebook/react-native/pull/12626

Differential Revision: D4632036

Pulled By: hramos

fbshipit-source-id: 2d1ad890e78e457205179e36c3ef04ffec354ad9
2017-03-09 13:49:46 -08:00
Hector Ramos a44c5e6b5c Refresh website
Reviewed By: mkonicek

Differential Revision: D4634272

fbshipit-source-id: 97d22115ffa29456ce253ad8c97a720d0c4e4d53
2017-03-09 13:42:33 -08:00
Héctor RamosandHector Ramos 7460169372 Refreshed design.
Summary:
This is #12292, but squashed into a single commit. This should help land the PR cleanly, as the commit history for #12292 was in a bad state: see how 1cbf79d456 expects `"mkdirp": "^0.5.1"` in `package.json`, but the earlier 66920e4e4b had changed this to `"mkdirp": "*",`.

Verified the website built and run.
Confirmed docs, apis, components were generated.
Confirmed landing page, showcase, blog, docs, help page all load and look as expected.
Closes https://github.com/facebook/react-native/pull/12575

Differential Revision: D4617979

Pulled By: hramos

fbshipit-source-id: 86e2d0b36ccd25fc7f2e9f3b1351f97e7ba64d57
2017-03-09 13:41:30 -08:00
Andrew RasmussenandHector Ramos 969d8c4be1 Product Pains -> Canny
Summary:
We migrated everyone on Product Pains over to our new service, Canny. We also moved every product's feedback to a product-specific subdomain (eg. https://react-native.canny.io/feature-requests).

This PR updates every Product Pains URL over to its new Canny version.

Changes only affect docs, blog posts, and bot responses - not the React Native library itself.

**Test plan**

I visited https://react-native.canny.io/feature-requests. Since there are no code changes this seems sufficient but let me know if I need to run the blog website or something.
Closes https://github.com/facebook/react-native/pull/12429

Differential Revision: D4581492

Pulled By: hramos

fbshipit-source-id: 7d124ab7ed9228d47f1bc4417d8992f15ff17f01
2017-03-09 13:41:21 -08:00
James IdeandHector Ramos e55fcc5a4b Some more Exponent -> Expo updates
Summary:
Updates the remaining references to Exponent to Expo, similar in spirit to b698b4ad62. Left the filename of the blog post alone so that permalinks keep working.
Closes https://github.com/facebook/react-native/pull/12776

Differential Revision: D4676911

Pulled By: hramos

fbshipit-source-id: 5b8b8413f95c984df4157e4e1634dd35a78afa29
2017-03-09 13:15:41 -08:00
iboxgithubandHector Ramos 879d1c1dba Update Troubleshooting.md
Summary:
Following ericvicenti request on https://github.com/facebook/react-native/issues/10188#issuecomment-282448021
Hope it helps

Thanks for submitting a pull request! Please provide enough information so that others can review your pull request:

> **Unless you are a React Native release maintainer and cherry-picking an *existing* commit into a current release, ensure your pull request is targeting the `master` React Native branch.**

Explain the **motivation** for making this change. What existing problem does the pull request solve?

Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it.

**Test plan (required)**

Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.

Make sure tests pass on both Travis and Circle CI.

**Code formatting**

Look around. Match the style of the rest of the codebase. See also
Closes https://github.com/facebook/react-native/pull/12578

Differential Revision: D4682076

Pulled By: mkonicek

fbshipit-source-id: 2fab5355a066eb3d8333e50f07ab3a258cbc002b
2017-03-09 13:13:04 -08:00
Hector Ramos 5fc656dc12 Tweak presentation of Colors guide.
Summary:
Explain a bit more how these are used.
Closes https://github.com/facebook/react-native/pull/12753

Differential Revision: D4663972

Pulled By: hramos

fbshipit-source-id: acbda6593e3f2a01776d2208f6ef3bc69bca0eef
2017-03-08 17:30:16 -08:00
Michael JacksonandHector Ramos 41230fe352 Remove reference to react-router-native
Summary:
We're currently taking ownership of react-router-native and have shipped several beta releases that do not wrap the NavigationExperimental API. In order to avoid confusion, I'd appreciate it if we could avoid mentioning react-router-native here. Thanks :)
Closes https://github.com/facebook/react-native/pull/12691

Differential Revision: D4654551

Pulled By: hramos

fbshipit-source-id: d1f393cefc87f087d4e9c19b7cbee861f0a2d29a
2017-03-08 17:27:05 -08:00
Jonathan SagorinandHector Ramos 98aea8cdf0 Update State.md
Summary:
Changes to setState will cause re-renders.
**Test plan (required)**
Textual changes only - on Markdown only

Make sure tests pass on both Travis and Circle CI.

**Code formatting**

Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#style-guide).

For more info, see the ["Pull Requests" section of our "Contributing" guidelines](https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#pull-requests).
Closes https://github.com/facebook/react-native/pull/12647

Differential Revision: D4658053

Pulled By: ericvicenti

fbshipit-source-id: 0d9cde8f9710b4e0f7d7f82214d8eeda0bf3c135
2017-03-08 17:26:59 -08:00
Esa Juhana LahikainenandHector Ramos cd8b6ff5ac Improve iOS' running on device instructions
Summary:
Add missing step needed to run your project on a device

Thanks for submitting a pull request! Please provide enough information so that others can review your pull request:

> **Unless you are a React Native release maintainer and cherry-picking an *existing* commit into a current release, ensure your pull request is targeting the `master` React Native branch.**

Explain the **motivation** for making this change. What existing problem does the pull request solve?

Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it.

**Test plan (required)**

Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.

Make sure tests pass on both Travis and Circle CI.

**Code formatting**

Look around. Match the style of the rest of the codebase. See also the simple [style guide](https://github.com/facebook/react-native/blob
Closes https://github.com/facebook/react-native/pull/12718

Differential Revision: D4657289

Pulled By: ericvicenti

fbshipit-source-id: f73736027d3872c63046c8a46307e9b486b4d444
2017-03-08 17:26:53 -08:00
Jack StevensandHector Ramos 7d09e501b5 Remove semi-colons after class definition
Summary:
None of the preceding code examples have semi-colons after the class definition (because they're not necessary):

- https://facebook.github.io/react-native/docs/tutorial.html
- https://facebook.github.io/react-native/docs/props.html
- https://facebook.github.io/react-native/docs/state.html
- https://facebook.github.io/react-native/docs/style.html

For consistency, I removed them 👍
Closes https://github.com/facebook/react-native/pull/12616

Differential Revision: D4651704

fbshipit-source-id: e59fa37c82d61728bc8ee28c7109717bd10635c2
2017-03-08 17:26:05 -08:00
Hector Ramos de8ec5afd9 Update Getting Started instructions.
Summary:
I am starting fresh on a new Mac and took another stab at the instructions.

- Java SDK 7 seems to work just fine.
- Adjusted Custom Android Studio installation instructions to use bare minimum required.
- Updated AVD configuration instructions with necessary changes to run AVD the first time.
- Added note on using real devices.
- Removed notes warning against use of API Level 23, these do not belong on a Getting Started guide.
- Added step to install Xcode Command Line Tools.
- Use `.profile` over `.bashrc` as the latter is shell-specific and is not loaded on login shells (e.g. new Terminal windows). `.profile` will work on bash, however, which is the default macOS shell.
- Added screenshots.

Went through steps for setting up RN for iOS as well as Android on a new Mac. Ended with apps running on the iOS Simulator and a AVD.

Generated website and verified instructions rendered correctly for each of the OS/device permutations.
Closes https://github.com/facebook/react-native/pull/12272

Differential Revision: D4637737

Pulled By: hramos

fbshipit-source-id: 5d322e4d55dbabc70f70471622a2f379ac6230cb
2017-03-08 17:25:44 -08:00
Eric VicentiandHector Ramos fce17ed847 Deprecate NavigationExperimental
Summary:
Docs change to encourage people to use React Navigation over other options.

Explains a bit of history about Navigator and NavigationExperimental.

Remove an intro guide that encourages use of Navigator. Hopefully the existing ReactNav guide fills this void.

Navigator docs are less emphasized but still present.

Reviewed By: mkonicek

Differential Revision: D4634452

fbshipit-source-id: 26763c2f02530009b3dfd20b0590fadcb5ea35ee
2017-03-08 17:24:14 -08:00
Ryan FlorenceandHector Ramos 26aa66091d remove more peculiar web language
Summary:
I guess we could ask Håkon Wium Lie if indeed it was unintended to have a set of rules be inherited or not, but this paragraph is a little bit weird 🤷‍♀️
Closes https://github.com/facebook/react-native/pull/12485

Differential Revision: D4608425

Pulled By: hramos

fbshipit-source-id: a346b1e65c9fc7b06634e529c1ef6f9e24794cca
2017-03-08 17:23:51 -08:00
Daniel FischerandHector Ramos 3b940301f3 Update UsingAScrollView.md
Summary:
Simple spacing update for consistency.

Super simple PR. I'm just fixing the spacing in the documentation to be consistent with the other docs.
Closes https://github.com/facebook/react-native/pull/12549

Differential Revision: D4608409

Pulled By: hramos

fbshipit-source-id: 9df0e3e0260d292e6788f32a713552c30240da47
2017-03-08 17:22:28 -08:00
Javier OlaecheaandHector Ramos 8b4ade2c6b Don't use 'vertically' when describing a ListView
Summary:
it gives the wrong impression that a ListView has the restriction to only align the items vertically

The problem the PR addresses is that the documentation currently gives the initial impression that a ListView can only stack the items vertically.
Closes https://github.com/facebook/react-native/pull/12564

Differential Revision: D4622096

Pulled By: hramos

fbshipit-source-id: ce634087d143a28904d998a4c7301ca18392714e
2017-03-08 17:22:22 -08:00
Ryan FlorenceandHector Ramos b3e0d6e89a remove peculiar web language
Summary:
Nothing about the web prevents people from providing good visual feedback on touch events. I'm not trying to be a punk, just coming across this language (several times now) gives me weird feelings since I come from "the web".
Closes https://github.com/facebook/react-native/pull/12484

Differential Revision: D4608430

Pulled By: hramos

fbshipit-source-id: 9ab72bffb300aa7bd76b6e07470dfe9763e50556
2017-03-08 17:22:11 -08:00
Héctor RamosandHector Ramos 66bb79a773 Recommend installing latest version of Xcode
Summary:
Xcode 8 is required as of 453e70d19c
Closes https://github.com/facebook/react-native/pull/12810

Differential Revision: D4677067

Pulled By: hramos

fbshipit-source-id: 6de932fe8a79b13f0bb13aba10baf2d1960f8a4f
2017-03-08 16:18:53 -08:00
Mike Grabowski 6e650937fd [0.42.0] Bump version numbers 2017-03-01 15:59:50 +01:00
James Ide 852775fd1a Support the --remote option in bump-oss-version.js script
Summary:
In my RN checkout, I use "upstream" as my remote instead of "origin" -> this lets me run `scripts/bump-oss-version.js --remote upstream 0.41.1` for example.

Also made the script executable so we don't need to put `node` in front of it, and updated the Releases.md doc.
Closes https://github.com/facebook/react-native/pull/12230

Differential Revision: D4515070

Pulled By: mkonicek

fbshipit-source-id: f218a6b77959588ee5f625b8589ac080dd010034
2017-02-28 11:40:43 -08:00
Mike Grabowski 3ccf8dfb09 [0.42.0-rc.3] Bump version numbers 2017-02-14 15:01:05 +01:00
Mike Grabowski d877c25cc4 Temp fix for the bump script 2017-02-14 14:59:41 +01:00
Mike Grabowski 9c2760ae20 Merge branch 2017-02-14 14:53:12 +01:00
Eloy DuránandMike Grabowski 2981560e40 Make podspec great again.
Summary:
Fixes #11272
Fixes #11572
Fixes #11781

The main changes here are:

* This depends on the latest CocoaPods (1.2.0). It’s currently in RC, but if I’m not mistaken a proper release is expected soon. /cc dantoml
* Adds required header search paths for the jschelpers and cxxreact subspecs.
* Makes the jschelpers and cxxreact headers private to building React Native, not visible to the user’s project.
* It uses the canonical upstream Yoga v1.0.0 podspec: https://github.com/facebook/yoga/blob/master/Yoga.podspec
* Consistent styling.

I have been able to get our app to build again using this https://github.com/artsy/emission/pull/437. The spec has some warnings, but otherwise fully passes lint.

rh389 sjmueller Could you please test with your projects?
Closes https://github.com/facebook/react-native/pull/12089

Differential Revision: D4518605

fbshipit-source-id: ecf86232d8b1af52d139eadd1acc10f5c1d42c29
2017-02-14 14:50:47 +01:00
James Ide ead6dcd092 [React] Update React version to ~15.4.1 instead of the RC
React 15.4.x has been out for awhile. Test Plan: open UI Explorer, go through the various screens and look for warnings or errors.
2017-02-08 17:51:51 -08:00
Martin Konicek 363b904d59 [0.42.0-rc.2] Bump version numbers 2017-02-08 12:03:16 +00:00
Martin Konicek 021e754c8f CLI: Add README to app template, lint
Reviewed By: mkonicek

Differential Revision:
D4522116
Ninja: oss only

fbshipit-source-id: d17e0e8badcfe97eaea092e5d5274e02904a534d
2017-02-08 11:47:00 +00:00
Martin Konicek 2bc2ae761a CLI: Update Navigation app template
Summary:
Update the template that will be used by `react-native init --template navigation`. The goal is to make it easier for people to get started by demonstrating a few very basic concepts such as navigation, lists and text input.

**Test plan (required)**

<img src="https://cloud.githubusercontent.com/assets/346214/22697898/ced66f52-ed4a-11e6-9b90-df6daef43199.gif" alt="Android Example" height="800" style="float: left"/>

<img src="https://cloud.githubusercontent.com/assets/346214/22697901/cfeab3e4-ed4a-11e6-8552-d76585317ac2.gif" alt="iOS Example" height="800"/>
Closes https://github.com/facebook/react-native/pull/12260

Differential Revision: D4521758

Pulled By: mkonicek

fbshipit-source-id: d7d9e481dd3373917ac68ec9169b9ac3267547a9
2017-02-08 11:46:53 +00:00
Martin Konicek 3a6dff4f4f CLI: Add support for project templates
Summary:
Currently it is not trivial for people to get started with React Native. `react-native init MyApp` just creates a simple app with a single screen. People have to spend time figuring out how to add more screens, or how to accomplish very basic tasks such as rendering a list of data or handling text input.

Let's add an option: `react-native init --template navigation` - this creates a "starter" app which can be easily tweaked into the actual app the person wants to build.

**Test plan (required)**

- Checked that 'react-native init MyApp' still works as before:

<img width="487" alt="screenshot 2017-02-02 16 56 28" src="https://cloud.githubusercontent.com/assets/346214/22559344/b2348ebe-e968-11e6-9032-d1c33216f490.png">

<img width="603" alt="screenshot 2017-02-02 16 58 04" src="https://cloud.githubusercontent.com/assets/346214/22559370/c96a2ca6-e968-11e6-91f7-7afb967920fc.png">

- Ran 'react-native init MyNavApp --template'. This prints the available templates:

```
$ react-native init MyNavApp
Closes https://github.com/facebook/react-native/pull/12170

Differential Revision: D4516241

Pulled By: mkonicek

fbshipit-source-id: 8ac081157919872e92947ed64ea64fb48078614d
2017-02-08 11:46:41 +00:00
Mike Grabowski ff8257c47e [0.42.0-rc.1] Bump version numbers 2017-02-03 09:19:19 +01:00
Mike Grabowski 0ec85db3bf Remove bundle.js 2017-02-03 09:18:48 +01:00
Mike Grabowski 26060a4526 [0.42.0-rc.0] Bump version numbers 2017-02-02 18:47:00 +01:00
121 changed files with 5780 additions and 25637 deletions
+2
View File
@@ -21,6 +21,7 @@ script:
- if [[ "$TEST_TYPE" = e2e-objc-tvos ]]; then node ./scripts/run-ci-e2e-tests.js --tvos --retries 3; fi
- if [[ "$TEST_TYPE" = js ]]; then npm run flow check; fi
- if [[ "$TEST_TYPE" = js ]]; then npm test -- --maxWorkers=1; fi
- if [[ ( "$TEST_TYPE" = podspecs ) && ( "$TRAVIS_PULL_REQUEST" = "false" ) ]]; then gem install cocoapods && ./scripts/process-podspecs.sh; fi
env:
matrix:
@@ -29,6 +30,7 @@ env:
- TEST_TYPE=objc-ios
- TEST_TYPE=objc-tvos
- TEST_TYPE=js
- TEST_TYPE=podspecs
branches:
only:
@@ -26,8 +26,7 @@ const PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
/**
* Do not use unless you have a very good reason. All the elements that
* respond to press should have a visual feedback when touched. This is
* one of the primary reasons a "web" app doesn't feel "native".
* respond to press should have a visual feedback when touched.
*
* > **NOTE**: TouchableWithoutFeedback supports only one child
* >
+10 -3
View File
@@ -394,6 +394,13 @@ const textColor = 'white';
const rowGutter = 1;
const rowHeight = 46;
// For unknown reasons, setting elevation: Number.MAX_VALUE causes remote debugging to
// hang on iOS (some sort of overflow maybe). Setting it to Number.MAX_SAFE_INTEGER fixes
// the iOS issue, but since elevation is an Android-only style property we should only
// use it on Android.
// See: https://github.com/facebook/react-native/issues/12223
const elevation = Platform.OS === 'android' ? Number.MAX_SAFE_INTEGER : undefined;
var styles = StyleSheet.create({
fullScreen: {
backgroundColor: 'transparent',
@@ -402,13 +409,13 @@ var styles = StyleSheet.create({
right: 0,
top: 0,
bottom: 0,
elevation: Number.MAX_VALUE
elevation: elevation,
},
inspector: {
backgroundColor: backgroundColor(0.95),
flex: 1,
paddingTop: 5,
elevation: Number.MAX_VALUE
elevation: elevation,
},
inspectorButtons: {
flexDirection: 'row',
@@ -456,7 +463,7 @@ var styles = StyleSheet.create({
left: 0,
right: 0,
bottom: 0,
elevation: Number.MAX_VALUE
elevation: elevation,
},
listRow: {
position: 'relative',
+2 -2
View File
@@ -32,7 +32,7 @@ Please use these community resources for getting help. We use the GitHub issues
- Ask a question on [StackOverflow](https://stackoverflow.com/) and tag it with `react-native`
- Chat with us on [Reactiflux](https://discord.gg/0ZcbPKXt5bWJVmUY) in #react-native
- Articulate your feature request or upvote existing ones on [Product Pains](https://productpains.com/product/react-native/)
- Articulate your feature request or upvote existing ones on [Canny](https://react-native.canny.io/feature-requests)
- Start a thread on the [React Discussion Board](https://discuss.reactjs.org/)
- Join #reactnative on IRC: chat.freenode.net
- If it turns out that you may have found a bug, please [open an issue](#opening-issues)
@@ -84,7 +84,7 @@ React Native is under active development. See the guide on [upgrading React Nati
If you encounter a bug with React Native we would like to hear about it. Search the [existing issues](https://github.com/facebook/react-native/issues) and try to make sure your problem doesnt already exist before opening a new issue. Its helpful if you include the version of React Native and OS youre using. Please include a stack trace and reduced repro case when appropriate, too.
The GitHub issues are intended for bug reports and feature requests. For help and questions with using React Native please make use of the resources listed in the [Getting Help](#getting-help) section. [Product Pains](https://productpains.com/product/react-native/) in particular is a good way to signal your interest in a feature or issue. There are limited resources available for handling issues and by keeping the list of open issues lean we can respond in a timely manner.
The GitHub issues are intended for bug reports and feature requests. For help and questions with using React Native please make use of the resources listed in the [Getting Help](#getting-help) section. [Canny](https://react-native.canny.io/feature-requests) in particular is a good way to signal your interest in a feature or issue. There are limited resources available for handling issues and by keeping the list of open issues lean we can respond in a timely manner.
## Contributing
+96 -98
View File
@@ -1,140 +1,138 @@
require 'json'
require "json"
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
Pod::Spec.new do |s|
s.name = "React"
s.version = package['version']
s.summary = package['description']
s.description = <<-DESC
React Native apps are built using the React JS
framework, and render directly to native UIKit
elements using a fully asynchronous architecture.
There is no browser and no HTML. We have picked what
we think is the best set of features from these and
other technologies to build what we hope to become
the best product development framework available,
with an emphasis on iteration speed, developer
delight, continuity of technology, and absolutely
beautiful and fast products with no compromises in
quality or capability.
DESC
s.homepage = "http://facebook.github.io/react-native/"
s.license = package['license']
s.author = "Facebook"
s.source = { :git => "https://github.com/facebook/react-native.git", :tag => "v#{s.version}" }
s.default_subspec = 'Core'
s.requires_arc = true
s.platform = :ios, "8.0"
s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => "c++14" }
s.header_dir = 'React'
s.preserve_paths = "package.json", "LICENSE", "LICENSE-CustomComponents", "PATENTS"
s.name = "React"
s.version = "0.42.3"
s.summary = package["description"]
s.description = <<-DESC
React Native apps are built using the React JS
framework, and render directly to native UIKit
elements using a fully asynchronous architecture.
There is no browser and no HTML. We have picked what
we think is the best set of features from these and
other technologies to build what we hope to become
the best product development framework available,
with an emphasis on iteration speed, developer
delight, continuity of technology, and absolutely
beautiful and fast products with no compromises in
quality or capability.
DESC
s.homepage = "http://facebook.github.io/react-native/"
s.license = package["license"]
s.author = "Facebook"
s.source = { :git => "https://github.com/facebook/react-native.git", :tag => "v#{s.version}" }
s.default_subspec = "Core"
s.requires_arc = true
s.platform = :ios, "8.0"
s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => "c++14" }
s.preserve_paths = "package.json", "LICENSE", "LICENSE-CustomComponents", "PATENTS"
s.cocoapods_version = ">= 1.2.0"
s.subspec 'Core' do |ss|
ss.dependency 'React/yoga'
ss.dependency 'React/cxxreact'
ss.source_files = "React/**/*.{c,h,m,mm,S}"
ss.exclude_files = "**/__tests__/*", "IntegrationTests/*", "React/**/RCTTVView.*", "ReactCommon/yoga/*"
ss.frameworks = "JavaScriptCore"
ss.libraries = "stdc++"
s.subspec "Core" do |ss|
ss.dependency "Yoga", "#{package["version"]}.React"
ss.dependency "React/cxxreact"
ss.source_files = "React/**/*.{c,h,m,mm,S}"
ss.exclude_files = "**/__tests__/*", "IntegrationTests/*", "React/**/RCTTVView.*", "ReactCommon/yoga/*", "React/Cxx*/*"
ss.framework = "JavaScriptCore"
ss.libraries = "stdc++"
end
s.subspec 'tvOS' do |ss|
ss.dependency 'React/Core'
ss.source_files = "React/**/RCTTVView.{h, m}"
s.subspec "tvOS" do |ss|
ss.dependency "React/Core"
ss.source_files = "React/**/RCTTVView.{h, m}"
end
s.subspec 'jschelpers' do |ss|
ss.source_files = 'ReactCommon/jschelpers/{JavaScriptCore,JSCWrapper}.{cpp,h}'
ss.header_dir = 'jschelpers'
s.subspec "jschelpers" do |ss|
ss.source_files = "ReactCommon/jschelpers/{JavaScriptCore,JSCWrapper}.{cpp,h}"
ss.private_header_files = "ReactCommon/jschelpers/{JavaScriptCore,JSCWrapper}.h"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "$(PODS_TARGET_SRCROOT)/ReactCommon" }
ss.framework = "JavaScriptCore"
end
s.subspec 'cxxreact' do |ss|
ss.dependency 'React/jschelpers'
ss.source_files = 'ReactCommon/cxxreact/{JSBundleType,oss-compat-util}.{cpp,h}'
ss.header_dir = 'cxxreact'
s.subspec "cxxreact" do |ss|
ss.dependency "React/jschelpers"
ss.source_files = "ReactCommon/cxxreact/{JSBundleType,oss-compat-util}.{cpp,h}"
ss.private_header_files = "ReactCommon/cxxreact/{JSBundleType,oss-compat-util}.h"
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "$(PODS_TARGET_SRCROOT)/ReactCommon" }
end
s.subspec 'yoga' do |ss|
ss.source_files = 'ReactCommon/yoga/**/*.{c,h}'
ss.header_dir = 'yoga'
s.subspec "ART" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/ART/**/*.{h,m}"
end
s.subspec 'ART' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/ART/**/*.{h,m}"
s.subspec "RCTActionSheet" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/ActionSheetIOS/*.{h,m}"
end
s.subspec 'RCTActionSheet' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/ActionSheetIOS/*.{h,m}"
s.subspec "RCTAdSupport" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/AdSupport/*.{h,m}"
end
s.subspec 'RCTAdSupport' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/AdSupport/*.{h,m}"
s.subspec "RCTAnimation" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/NativeAnimation/{Drivers/*,Nodes/*,*}.{h,m}"
end
s.subspec 'RCTAnimation' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/NativeAnimation/{Drivers/*,Nodes/*,*}.{h,m}"
s.subspec "RCTCameraRoll" do |ss|
ss.dependency "React/Core"
ss.dependency "React/RCTImage"
ss.source_files = "Libraries/CameraRoll/*.{h,m}"
end
s.subspec 'RCTCameraRoll' do |ss|
ss.dependency 'React/Core'
ss.dependency 'React/RCTImage'
ss.source_files = "Libraries/CameraRoll/*.{h,m}"
s.subspec "RCTGeolocation" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/Geolocation/*.{h,m}"
end
s.subspec 'RCTGeolocation' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/Geolocation/*.{h,m}"
s.subspec "RCTImage" do |ss|
ss.dependency "React/Core"
ss.dependency "React/RCTNetwork"
ss.source_files = "Libraries/Image/*.{h,m}"
end
s.subspec 'RCTImage' do |ss|
ss.dependency 'React/Core'
ss.dependency 'React/RCTNetwork'
ss.source_files = "Libraries/Image/*.{h,m}"
s.subspec "RCTNetwork" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/Network/*.{h,m,mm}"
end
s.subspec 'RCTNetwork' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/Network/*.{h,m,mm}"
s.subspec "RCTPushNotification" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/PushNotificationIOS/*.{h,m}"
end
s.subspec 'RCTPushNotification' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/PushNotificationIOS/*.{h,m}"
s.subspec "RCTSettings" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/Settings/*.{h,m}"
end
s.subspec 'RCTSettings' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/Settings/*.{h,m}"
s.subspec "RCTText" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/Text/*.{h,m}"
end
s.subspec 'RCTText' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/Text/*.{h,m}"
s.subspec "RCTVibration" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/Vibration/*.{h,m}"
end
s.subspec 'RCTVibration' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/Vibration/*.{h,m}"
s.subspec "RCTWebSocket" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/WebSocket/*.{h,m}"
end
s.subspec 'RCTWebSocket' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/WebSocket/*.{h,m}"
s.subspec "RCTLinkingIOS" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/LinkingIOS/*.{h,m}"
end
s.subspec 'RCTLinkingIOS' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/LinkingIOS/*.{h,m}"
end
s.subspec 'RCTTest' do |ss|
ss.dependency 'React/Core'
ss.source_files = "Libraries/RCTTest/**/*.{h,m}"
ss.frameworks = "XCTest"
s.subspec "RCTTest" do |ss|
ss.dependency "React/Core"
ss.source_files = "Libraries/RCTTest/**/*.{h,m}"
ss.frameworks = "XCTest"
end
end
+1 -1
View File
@@ -411,7 +411,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder:(nonnull NSCoder *)aDecoder)
{
super.frame = frame;
if (self.reactTag && _bridge.isValid) {
[_bridge.uiManager setFrame:frame forView:self];
[_bridge.uiManager setSize:frame.size forView:self];
}
}
+1 -1
View File
@@ -56,7 +56,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:coder)
- (void)notifyForBoundsChange:(CGRect)newBounds
{
if (_reactSubview && _isPresented) {
[_bridge.uiManager setFrame:newBounds forView:_reactSubview];
[_bridge.uiManager setSize:newBounds.size forView:_reactSubview];
[self notifyForOrientationChange];
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION_NAME=1000.0.0-master
VERSION_NAME=0.42.3
GROUP=com.facebook.react
POM_NAME=ReactNative
+44
View File
@@ -0,0 +1,44 @@
package = JSON.parse(File.read(File.expand_path('../../package.json', __dir__)))
version = package['version']
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip
else
source[:tag] = "v#{version}"
end
Pod::Spec.new do |spec|
spec.name = 'Yoga'
spec.version = "#{version}.React"
spec.license = { :type => 'BSD' }
spec.homepage = 'https://facebook.github.io/yoga/'
spec.documentation_url = 'https://facebook.github.io/yoga/docs/api/c/'
spec.summary = 'Yoga is a cross-platform layout engine which implements Flexbox.'
spec.description = 'Yoga is a cross-platform layout engine enabling maximum collaboration within your team by implementing an API many designers are familiar with, and opening it up to developers across different platforms.'
spec.authors = 'Facebook'
spec.source = source
spec.module_name = 'yoga'
spec.requires_arc = false
spec.compiler_flags = [
'-fno-omit-frame-pointer',
'-fexceptions',
'-Wall',
'-Werror',
'-std=c11',
'-fPIC'
]
# Pinning to the same version as React.podspec.
spec.platform = :ios, "8.0"
# Set this environment variable when not using the `:path` option to install the pod.
# E.g. when publishing this spec to a spec repo.
source_files = 'yoga/**/*.{c,h}'
source_files = File.join('ReactCommon/yoga', source_files) if ENV['INSTALL_YOGA_WITHOUT_PATH_OPTION']
spec.source_files = source_files
end
+7 -6
View File
@@ -44,8 +44,9 @@ Run:
git checkout -b <version_you_are_releasing>-stable
# e.g. git checkout -b 0.22-stable
node ./scripts/bump-oss-version.js <exact-version_you_are_releasing>
# e.g. node ./scripts/bump-oss-version.js 0.22.0-rc
./scripts/bump-oss-version.js <exact-version_you_are_releasing>
# e.g. ./scripts/bump-oss-version.js 0.22.0-rc
# You can use the --remote option to specify a Git remote other than the default "origin"
```
Circle CI will automatically run the tests and publish to npm with the version you have specified (e.g `0.22.0-rc`) and tag `next` meaning that this version will not be installed for users by default.
@@ -112,8 +113,8 @@ git cherry-pick commitHash1
If everything worked:
```bash
node ./scripts/bump-oss-version.js <exact_version_you_are_releasing>
# e.g. node ./scripts/bump-oss-version.js 0.28.0-rc.1
./scripts/bump-oss-version.js <exact_version_you_are_releasing>
# e.g. ./scripts/bump-oss-version.js 0.28.0-rc.1
````
-------------------
@@ -141,8 +142,8 @@ git cherry-pick commitHash1
If everything worked:
```bash
node ./scripts/bump-oss-version.js <exact_version_you_are_releasing>
# e.g. node ./scripts/bump-oss-version.js 0.22.0
./scripts/bump-oss-version.js <exact_version_you_are_releasing>
# e.g. ./scripts/bump-oss-version.js 0.22.0
```
#### Update the release notes
@@ -64,6 +64,6 @@ Your feedback lets us know what we should prioritize. I know some people will re
If you find a mistake in the documentation, like inaccurate descriptions or code that doesn't actually work, [file an issue](https://github.com/facebook/react-native/issues). Tag it with "Documentation", so that it's easier to route it to the right people.
If there isn't a specific mistake, but something in the documentation is fundamentally confusing, it's not a great fit for a GitHub issue. Instead, complain on [Product Pains](https://productpains.com/product/react-native/) about the area of the docs that could use help. This helps us prioritize when we are doing more general work like guide-writing.
If there isn't a specific mistake, but something in the documentation is fundamentally confusing, it's not a great fit for a GitHub issue. Instead, post on [Canny](https://react-native.canny.io/feature-requests) about the area of the docs that could use help. This helps us prioritize when we are doing more general work like guide-writing.
Thanks for reading this far, and thanks for using React Native!
@@ -1,5 +1,5 @@
---
title: Exponent Talks: Adam on Unraveling Navigation
title: Expo Talks: Adam on Unraveling Navigation
author: Héctor Ramos
authorTitle: Developer Advocate at Facebook
authorURL: https://twitter.com/hectorramos
@@ -9,4 +9,4 @@ youtubeVideoId: oeSjTxVkMhc
category: videos
---
[Adam Miskiewicz](https://twitter.com/skevy) from [Exponent](http://getexponent.com/) talks about mobile navigation and the [`ex-navigation`](https://github.com/exponentjs/ex-navigation) React Native library at Exponent's office hours last week.
[Adam Miskiewicz](https://twitter.com/skevy) from [Expo](https://expo.io/) talks about mobile navigation and the [`ex-navigation`](https://github.com/exponent/ex-navigation) React Native library at Expo's office hours last week.
@@ -14,7 +14,7 @@ We have heard from many people that there is so much work happening with React N
- **Stability**. Improve the underlying infrastructure to reduce bugs and improve code quality.
- **Developer Experience**. Help React Native developers move faster
If you have suggestions for features that you think would be valuable on the roadmap, check out [Product Pains](https://productpains.com/product/react-native), where you can suggest new features and discuss existing proposals.
If you have suggestions for features that you think would be valuable on the roadmap, check out [Canny](https://react-native.canny.io/feature-requests), where you can suggest new features and discuss existing proposals.
## What's new in React Native
+1 -1
View File
@@ -10,7 +10,7 @@ category: announcements
Shortly after React Native was introduced, we started releasing every two weeks to help the community adopt new features, while keeping versions stable for production use. At Facebook we had to stabilize the codebase every two weeks for the release of our production iOS apps, so we decided to release the open source versions at the same pace. Now, many of the Facebook apps ship once per week, especially on Android. Because we ship from master weekly, we need to keep it quite stable. So the bi-weekly release cadence doesn't even benefit internal contributors anymore.
We frequently hear feedback from the community that the release rate is hard to keep up with. Tools like [Exponent](https://getexponent.com/) had to skip every other release in order to manage the rapid change in version. So it seems clear that the bi-weekly releases did not serve the community well.
We frequently hear feedback from the community that the release rate is hard to keep up with. Tools like [Expo](https://expo.io/) had to skip every other release in order to manage the rapid change in version. So it seems clear that the bi-weekly releases did not serve the community well.
### Now releasing monthly
@@ -0,0 +1,158 @@
---
title: Using Native Driver for Animated
author: Janic Duplessis
authorTitle: Software Engineer at App & Flow
authorURL: https://twitter.com/janicduplessis
authorImage: https://secure.gravatar.com/avatar/8d6b6c0f5b228b0a8566a69de448b9dd?s=128
authorTwitter: janicduplessis
category: engineering
---
For the past year, we've been working on improving performance of animations that use the Animated library. Animations are very important to create a beautiful user experience but can also be hard to do right. We want to make it easy for developers to create performant animations without having to worry about some of their code causing it to lag.
## What is this?
The Animated API was designed with a very important constraint in mind, it is serializable. This means we can send everything about the animation to native before it has even started and allows native code to perform the animation on the UI thread without having to go through the bridge on every frame. It is very useful because once the animation has started, the JS thread can be blocked and the animation will still run smoothly. In practice this can happen a lot because user code runs on the JS thread and React renders can also lock JS for a long time.
## A bit of history...
This project started about a year ago, when Expo built the li.st app on Android. [Krzysztof Magiera](https://twitter.com/kzzzf) was contracted to build the initial implementation on Android. It ended up working well and li.st was the first app to ship with native driven animations using Animated. A few months later, [Brandon Withrow](https://github.com/buba447) built the initial implementation on iOS. After that, [Ryan Gomba](https://twitter.com/ryangomba) and myself worked on adding missing features like support for `Animated.event` as well as squash bugs we found when using it in production apps. This was truly a community effort and I would like to thanks everyone that was involved as well as Expo for sponsoring a large part of the development. It is now used by `Touchable` components in React Native as well as for navigation animations in the newly released [React Navigation](https://github.com/react-community/react-navigation) library.
## How does it work?
First, let's check out how animations currently work using Animated with the JS driver. When using Animated, you declare a graph of nodes that represent the animations that you want to perform, and then use a driver to update an Animated value using a predefined curve. You may also update an Animated value by connecting it to an event of a `View` using `Animated.event`.
![](/react-native/blog/img/animated-diagram.png)
Here's a breakdown of the steps for an animation and where it happens:
- JS: The animation driver uses `requestAnimationFrame` to execute on every frame and update the value it drives using the new value it calculates based on the animation curve.
- JS: Intermediate values are calculated and passed to a props node that is attached to a `View`.
- JS: The `View` is updated using `setNativeProps`.
- JS to Native bridge.
- Native: The `UIView` or `android.View` is updated.
As you can see, most of the work happens on the JS thread. If it is blocked the animation will skip frames. It also needs to go through the JS to Native bridge on every frame to update native views.
What the native driver does is move all of these steps to native. Since Animated produces a graph of animated nodes, it can be serialized and sent to native only once when the animation starts, eliminating the need to callback into the JS thread; the native code can take care of updating the views directly on the UI thread on every frame.
Here's an example of how we can serialize an animated value and an interpolation node (not the exact implementation, just an example).
Create the native value node, this is the value that will be animated:
```
NativeAnimatedModule.createNode({
id: 1,
type: 'value',
initialValue: 0,
});
```
Create the native interpolation node, this tells the native driver how to interpolate a value:
```
NativeAnimatedModule.createNode({
id: 2,
type: 'interpolation',
inputRange: [0, 10],
outputRange: [10, 0],
extrapolate: 'clamp',
});
```
Create the native props node, this tells the native driver which prop on the view it is attached to:
```
NativeAnimatedModule.createNode({
id: 3,
type: 'props',
properties: ['style.opacity'],
});
```
Connect nodes together:
```
NativeAnimatedModule.connectNodes(1, 2);
NativeAnimatedModule.connectNodes(2, 3);
```
Connect the props node to a view:
```
NativeAnimatedModule.connectToView(3, ReactNative.findNodeHandle(viewRef));
```
With that, the native animated module has all the info it needs to update the native views directly without having to go to JS to calculate any value.
All there is left to do is actually start the animation by specifying what type of animation curve we want and what animated value to update. Timing animations can also be simplified by calculating every frame of the animation in advance in JS to make the native implementation smaller.
```
NativeAnimatedModule.startAnimation({
type: 'timing',
frames: [0, 0.1, 0.2, 0.4, 0.65, ...],
animatedValueId: 1,
});
```
And now here's the breakdown of what happens when the animation runs:
- Native: The native animation driver uses `CADisplayLink` or `android.view.Choreographer` to execute on every frame and update the value it drives using the new value it calculates based on the animation curve.
- Native: Intermediate values are calculated and passed to a props node that is attached to a native view.
- Native: The `UIView` or `android.View` is updated.
As you can see, no more JS thread and no more bridge which means faster animations! 🎉🎉
## How do I use this in my app?
For normal animations the answer is simple, just add `useNativeDriver: true` to the animation config when starting it.
Before:
```
Animated.timing(this.state.animatedValue, {
toValue: 1,
duration: 500,
}).start();
```
After:
```
Animated.timing(this.state.animatedValue, {
toValue: 1,
duration: 500,
useNativeDriver: true, // <-- Add this
}).start();
```
Animated values are only compatible with one driver so if you use native driver when starting an animation on a value, make sure every animation on that value also uses the native driver.
It also works with `Animated.event`, this is very useful if you have an animation that must follow the scroll position because without the native driver it will always run a frame behind of the gesture because of the async nature of React Native.
Before:
```
<ScrollView
scrollEventThrottle={16}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.animatedValue } } }]
)}
>
{content}
</ScrollView>
```
After:
```
<Animated.ScrollView // <-- Use the Animated ScrollView wrapper
scrollEventThrottle={1} // <-- Use 1 here to make sure no events are ever missed
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.animatedValue } } }],
{ useNativeDriver: true } // <-- Add this
)}
>
{content}
</Animated.ScrollView>
```
## Caveats
Not everything you can do with Animated is currently supported in Native Animated. The main limitation is that you can only animate non-layout properties, things like `transform`, `opacity` and `backgroundColor` will work but flexbox and position properties won't. Another one is with `Animated.event`, it will only work with direct events and not bubbling events. This means it does not work with `PanResponder` but does work with things like `ScrollView#onScroll`.
Native Animated has also been part of React Native for quite a while but has never been documented because it was considered experimental. Because of that make sure you are using a recent version (0.40+) of React Native if you want to use this feature.
## Resources
For more information about animated I recommend watching [this talk](https://www.youtube.com/watch?v=xtqUJVqpKNo) by [Christopher Chedeau](https://twitter.com/Vjeux).
If you want a deep dive into animations and how offloading them to native can improve user experience there is also [this talk](https://www.youtube.com/watch?v=qgSMjYWqBk4) by [Krzysztof Magiera](https://twitter.com/kzzzf).
+2 -2
View File
@@ -9,7 +9,7 @@ comment {author} tells me this issue is a duplicate of {match0}. Let's discuss t
close
@facebook-github-bot expected
comment The comment above tells me this is expected behavior. Closing this as we'd like to use the GitHub issue tracker for bugs. If you'd like to change how this feature works please post a feature request on [Product Pains](https://productpains.com/product/react-native/) so that other people can vote on it.
comment The comment above tells me this is expected behavior. Closing this as we'd like to use the GitHub issue tracker for bugs. If you'd like to change how this feature works please post a feature request on [Canny](https://react-native.canny.io/feature-requests) so that other people can vote on it.
close
@facebook-github-bot stack-overflow
@@ -36,7 +36,7 @@ comment Okay, reopening this issue.
reopen
@facebook-github-bot feature
comment Hey {issue_author}! Thanks for opening the issue, however it looks like a feature request. As noted in the [Issue template](https://github.com/facebook/react-native/blob/master/.github/ISSUE_TEMPLATE.md) we'd like to use the GitHub issues to track bugs only. Can you implement the feature as a standalone npm module? If not consider sending a pull request or a create an entry on [Product Pains](https://productpains.com/product/react-native). It has a voting system and if the feature gets upvoted enough it might get implemented. Closing this now, thanks for understanding!
comment Hey {issue_author}! Thanks for opening the issue, however it looks like a feature request. As noted in the [Issue template](https://github.com/facebook/react-native/blob/master/.github/ISSUE_TEMPLATE.md) we'd like to use the GitHub issues to track bugs only. Can you implement the feature as a standalone npm module? If not consider sending a pull request or a create an entry on [Canny](https://react-native.canny.io/feature-requests/). It has a voting system and if the feature gets upvoted enough it might get implemented. Closing this now, thanks for understanding!
close
@facebook-github-bot cla
+1 -1
View File
@@ -4,6 +4,6 @@ React Native, as you've probably heard, is getting really popular and truth is w
- If you **don't know how to do something** or **something is not working as you expect but not sure it's a bug**, please ask on [StackOverflow](http://stackoverflow.com/questions/tagged/react-native) with the tag `react-native` or for more real time interactions, ask on [Discord](https://discord.gg/0ZcbPKXt5bZjGY5n) in the #react-native channel.
- If this is a **feature request or a bug** that you would like to be fixed, please report it on [Product Pains](https://productpains.com/product/react-native/). It has a ranking feature that lets us focus on the most important issues the community is experiencing.
- If this is a **feature request or a bug** that you would like to be fixed, please report it on [Canny](https://react-native.canny.io/feature-requests). It has a ranking feature that lets us focus on the most important issues the community is experiencing.
- We welcome clear issues and PRs that are ready for in-depth discussion. Please provide **screenshots** where appropriate and always mention the **version** of React Native you're using. Thank you for your contributions!
+1 -1
View File
@@ -1 +1 @@
javascript:(function(){$('#new_comment_field')[0].value='Hey @' + $(".timeline-comment-header-text").first().find(".author").text() + '! Thanks for reporting this!\n\nThere\'s an awesome place to ask question like this one: [StackOverflow](http://stackoverflow.com/questions/tagged/react-native). It\'s the best system for Q&A. Many people from the community hang out there and will be able to see your question, you can vote on answers and mark question as answered etc. This lets us keep a list of bug reports and feature requests on github and especially [Product Pains](https://productpains.com/product/react-native/?tab=top) (again, with voting which is really nice).\n\nIf you think StackOverflow works for you please consider posting there instead and closing this issue.\n\nI\'m posting this here because github issues haven\'t been working very well for us and because StackOverflow is so much better. Thanks for reading! :)';$('button.btn-primary:contains("Comment")').click()})()
javascript:(function(){$('#new_comment_field')[0].value='Hey @' + $(".timeline-comment-header-text").first().find(".author").text() + '! Thanks for reporting this!\n\nThere\'s an awesome place to ask question like this one: [StackOverflow](http://stackoverflow.com/questions/tagged/react-native). It\'s the best system for Q&A. Many people from the community hang out there and will be able to see your question, you can vote on answers and mark question as answered etc. This lets us keep a list of bug reports and feature requests on github and especially [Canny](https://react-native.canny.io/feature-requests) (again, with voting which is really nice).\n\nIf you think StackOverflow works for you please consider posting there instead and closing this issue.\n\nI\'m posting this here because github issues haven\'t been working very well for us and because StackOverflow is so much better. Thanks for reading! :)';$('button.btn-primary:contains("Comment")').click()})()
+1 -1
View File
@@ -5,7 +5,7 @@ layout: docs
category: Guides
permalink: docs/accessibility.html
next: timers
previous: animations
previous: debugging
---
## Native App Accessibility (iOS and Android)
+1
View File
@@ -4,6 +4,7 @@ title: Building React Native from source
layout: docs
category: Guides (Android)
permalink: docs/android-building-from-source.html
banner: ejected
next: activityindicator
previous: android-ui-performance
---
+2 -160
View File
@@ -1,165 +1,7 @@
---
id: android-ui-performance
title: Profiling Android UI Performance
layout: docs
category: Guides (Android)
layout: redirect
permalink: docs/android-ui-performance.html
next: android-building-from-source
previous: signed-apk-android
destinationUrl: performance.html
---
We try our best to deliver buttery-smooth UI performance by default, but sometimes that just isn't possible. Remember, Android supports 10k+ different phones and is generalized to support software rendering: the framework architecture and need to generalize across many hardware targets unfortunately means you get less for free relative to iOS. But sometimes, there are things you can improve (and many times it's not native code's fault at all!).
The first step for debugging this jank is to answer the fundamental question of where your time is being spent during each 16ms frame. For that, we'll be using a standard Android profiling tool called systrace. But first...
> Make sure that JS dev mode is OFF!
>
> You should see `__DEV__ === false, development-level warning are OFF, performance optimizations are ON` in your application logs (which you can view using `adb logcat`)
## Profiling with Systrace
Systrace is a standard Android marker-based profiling tool (and is installed when you install the Android platform-tools package). Profiled code blocks are surrounded by markers start/end markers which are then visualized in a colorful chart format. Both the Android SDK and React Native framework provide standard markers that you can visualize.
### Collecting a trace
> NOTE:
>
> Systrace support was added in react-native `v0.15`. You will need to build with that version to collect a trace.
First, connect a device that exhibits the stuttering you want to investigate to your computer via USB and get it to the point right before the navigation/animation you want to profile. Run systrace as follows
```
$ <path_to_android_sdk>/platform-tools/systrace/systrace.py --time=10 -o trace.html sched gfx view -a <your_package_name>
```
A quick breakdown of this command:
- `time` is the length of time the trace will be collected in seconds
- `sched`, `gfx`, and `view` are the android SDK tags (collections of markers) we care about: `sched` gives you information about what's running on each core of your phone, `gfx` gives you graphics info such as frame boundaries, and `view` gives you information about measure, layout, and draw passes
- `-a <your_package_name>` enables app-specific markers, specifically the ones built into the React Native framework. `your_package_name` can be found in the `AndroidManifest.xml` of your app and looks like `com.example.app`
Once the trace starts collecting, perform the animation or interaction you care about. At the end of the trace, systrace will give you a link to the trace which you can open in your browser.
## Reading the trace
After opening the trace in your browser (preferably Chrome), you should see something like this:
![Example](img/SystraceExample.png)
If your trace .html file isn't opening correctly, check your browser console for the following:
![ObjectObserveError](img/ObjectObserveError.png)
Since Object.observe was deprecated in recent browsers, you may have to open the file from the Google Chrome Tracing tool. You can do so by:
- Opening tab in chrome chrome://tracing
- Selecting load
- Selecting the html file generated from the previous command.
**HINT**: Use the WASD keys to strafe and zoom
### Enable VSync highlighting
The first thing you should do is highlight the 16ms frame boundaries if you haven't already done that. Check this checkbox at the top right of the screen:
![Enable VSync Highlighting](img/SystraceHighlightVSync.png)
You should see zebra stripes as in the screenshot above. If you don't, try profiling on a different device: Samsung has been known to have issues displaying vsyncs while the Nexus series is generally pretty reliable.
### Find your process
Scroll until you see (part of) the name of your package. In this case, I was profiling `com.facebook.adsmanager`, which shows up as `book.adsmanager` because of silly thread name limits in the kernel.
On the left side, you'll see a set of threads which correspond to the timeline rows on the right. There are three/four threads we care about for our purposes: the UI thread (which has your package name or the name UI Thread), `mqt_js` and `mqt_native_modules`. If you're running on Android 5+, we also care about the Render Thread.
### UI Thread
This is where standard android measure/layout/draw happens. The thread name on the right will be your package name (in my case book.adsmanager) or UI Thread. The events that you see on this thread should look something like this and have to do with `Choreographer`, `traversals`, and `DispatchUI`:
![UI Thread Example](img/SystraceUIThreadExample.png)
### JS Thread
This is where JS is executed. The thread name will be either `mqt_js` or `<...>` depending on how cooperative the kernel on your device is being. To identify it if it doesn't have a name, look for things like `JSCall`, `Bridge.executeJSCall`, etc:
![JS Thread Example](img/SystraceJSThreadExample.png)
### Native Modules Thread
This is where native module calls (e.g. the `UIManager`) are executed. The thread name will be either `mqt_native_modules` or `<...>`. To identify it in the latter case, look for things like `NativeCall`, `callJavaModuleMethod`, and `onBatchComplete`:
![Native Modules Thread Example](img/SystraceNativeModulesThreadExample.png)
### Bonus: Render Thread
If you're using Android L (5.0) and up, you will also have a render thread in your application. This thread generates the actual OpenGL commands used to draw your UI. The thread name will be either `RenderThread` or `<...>`. To identify it in the latter case, look for things like `DrawFrame` and `queueBuffer`:
![Render Thread Example](img/SystraceRenderThreadExample.png)
## Identifying a culprit
A smooth animation should look something like the following:
![Smooth Animation](img/SystraceWellBehaved.png)
Each change in color is a frame -- remember that in order to display a frame, all our UI work needs to be done by the end of that 16ms period. Notice that no thread is working close to the frame boundary. An application rendering like this is rendering at 60FPS.
If you noticed chop, however, you might see something like this:
![Choppy Animation from JS](img/SystraceBadJS.png)
Notice that the JS thread is executing basically all the time, and across frame boundaries! This app is not rendering at 60FPS. In this case, **the problem lies in JS**.
You might also see something like this:
![Choppy Animation from UI](img/SystraceBadUI.png)
In this case, the UI and render threads are the ones that have work crossing frame boundaries. The UI that we're trying to render on each frame is requiring too much work to be done. In this case, **the problem lies in the native views being rendered**.
At this point, you'll have some very helpful information to inform your next steps.
## JS Issues
If you identified a JS problem, look for clues in the specific JS that you're executing. In the scenario above, we see `RCTEventEmitter` being called multiple times per frame. Here's a zoom-in of the JS thread from the trace above:
![Too much JS](img/SystraceBadJS2.png)
This doesn't seem right. Why is it being called so often? Are they actually different events? The answers to these questions will probably depend on your product code. And many times, you'll want to look into [shouldComponentUpdate](https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate).
> **TODO**: Add more tools for profiling JS
## Native UI Issues
If you identified a native UI problem, there are usually two scenarios:
1. the UI you're trying to draw each frame involves to much work on the GPU, or
2. You're constructing new UI during the animation/interaction (e.g. loading in new content during a scroll).
### Too much GPU work
In the first scenario, you'll see a trace that has the UI thread and/or Render Thread looking like this:
![Overloaded GPU](img/SystraceBadUI.png)
Notice the long amount of time spent in `DrawFrame` that crosses frame boundaries. This is time spent waiting for the GPU to drain its command buffer from the previous frame.
To mitigate this, you should:
- investigate using `renderToHardwareTextureAndroid` for complex, static content that is being animated/transformed (e.g. the `Navigator` slide/alpha animations)
- make sure that you are **not** using `needsOffscreenAlphaCompositing`, which is disabled by default, as it greatly increases the per-frame load on the GPU in most cases.
If these don't help and you want to dig deeper into what the GPU is actually doing, you can check out [Tracer for OpenGL ES](http://developer.android.com/tools/help/gltracer.html).
### Creating new views on the UI thread
In the second scenario, you'll see something more like this:
![Creating Views](img/SystraceBadCreateUI.png)
Notice that first the JS thread thinks for a bit, then you see some work done on the native modules thread, followed by an expensive traversal on the UI thread.
There isn't an easy way to mitigate this unless you're able to postpone creating new UI until after the interaction, or you are able to simplify the UI you're creating. The react native team is working on a infrastructure level solution for this that will allow new UI to be created and configured off the main thread, allowing the interaction to continue smoothly.
## Still stuck?
If you are confused or stuck, please post ask on [Stack Overflow with the react-native tag](http://stackoverflow.com/tags/react-native). If you are unable to get a response there, or find an issue with a core component, please [File a Github issue](https://github.com/facebook/react-native/issues).
+1 -1
View File
@@ -4,7 +4,7 @@ title: Animations
layout: docs
category: Guides
permalink: docs/animations.html
next: accessibility
next: navigation
previous: handling-touches
---
+30 -15
View File
@@ -4,26 +4,41 @@ title: Colors
layout: docs
category: Guides
permalink: docs/colors.html
next: images
previous: integration-with-existing-apps
next: platform-specific-code
previous: images
---
The following formats are supported:
Components in React Native are [styled using JavaScript](docs/styles.html). Color properties usually match how [CSS works on the web](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value).
- `'#f0f'` (#rgb)
- `'#f0fc'` (#rgba)
- `'#ff00ff'` (#rrggbb)
- `'#ff00ff00'` (#rrggbbaa)
- `'rgb(255, 255, 255)'`
- `'rgba(255, 255, 255, 1.0)'`
- `'hsl(360, 100%, 100%)'`
- `'hsla(360, 100%, 100%, 1.0)'`
- `'transparent'`
- `'red'`
- `0xff00ff00` (0xrrggbbaa)
### Red-green-blue
React Native supports `rgb()` and `rgba()` in both hexadecimal and functional notation:
For the named colors, React Native follows the [CSS3 specification](http://www.w3.org/TR/css3-color/#svg-color):
- `'#f0f'` (#rgb)
- `'#ff00ff'` (#rrggbb)
- `'rgb(255, 0, 255)'`
- `'rgba(255, 255, 255, 1.0)'`
- `'#f0ff'` (#rgba)
- `'#ff00ff00'` (#rrggbbaa)
### Hue-saturation-lightness
`hsl()` and `hsla()` is supported in functional notation:
- `'hsl(360, 100%, 100%)'`
- `'hsla(360, 100%, 100%, 1.0)'`
### `transparent`
This is a shortcut for `rgba(0,0,0,0)`:
- `'transparent'`
### Named colors
You can also use color names as values. React Native follows the [CSS3 specification](http://www.w3.org/TR/css3-color/#svg-color):
- <color aliceblue /> aliceblue (#f0f8ff)
- <color antiquewhite /> antiquewhite (#faebd7)
+2 -1
View File
@@ -4,8 +4,9 @@ title: Communication between native and React Native
layout: docs
category: Guides (iOS)
permalink: docs/communication-ios.html
banner: ejected
next: native-modules-android
previous: running-on-simulator-ios
previous: linking-libraries-ios
---
In [Integrating with Existing Apps guide](docs/integration-with-existing-apps.html) and [Native UI Components guide](docs/native-components-ios.html) we learn how to embed React Native in a native component and vice versa. When we mix native and React Native components, we'll eventually find a need to communicate between these two worlds. Some ways to achieve that have been already mentioned in other guides. This article summarizes available techniques.
+2 -2
View File
@@ -4,8 +4,8 @@ title: Debugging
layout: docs
category: Guides
permalink: docs/debugging.html
next: testing
previous: direct-manipulation
next: accessibility
previous: platform-specific-code
---
## Accessing the In-App Developer Menu
+2 -2
View File
@@ -4,8 +4,8 @@ title: Direct Manipulation
layout: docs
category: Guides
permalink: docs/direct-manipulation.html
next: debugging
previous: timers
next: performance
previous: javascript-environment
---
It is sometimes necessary to make changes directly to a component
+2 -2
View File
@@ -4,8 +4,8 @@ title: Gesture Responder System
layout: docs
category: Guides
permalink: docs/gesture-responder-system.html
next: native-modules-ios
previous: platform-specific-code
next: testing
previous: performance
---
The gesture responder system manages the lifecycle of gestures in your app. A touch can go through several phases as the app determines what the user's intention is. For example, the app needs to determine if the touch is scrolling, sliding on a widget, or tapping. This can even change during the duration of a touch. There can also be multiple simultaneous touches.
+32 -24
View File
@@ -144,7 +144,11 @@ npm install -g react-native-cli
### Xcode
The easiest way to install Xcode is via the [Mac App Store](https://itunes.apple.com/us/app/xcode/id497799835?mt=12). Installing Xcode will also install the iOS Simulator and all the necessary tools to build your iOS app.
The easiest way to install Xcode 8 is via the [Mac App Store](https://itunes.apple.com/us/app/xcode/id497799835?mt=12). Installing Xcode will also install the iOS Simulator and all the necessary tools to build your iOS app.
You will also need to install the Xcode Command Line Tools. Open Xcode, then choose "Preferences..." from the Xcode menu. Go to the Locations panel and install the tools by selecting the most recent version in the Command Line Tools dropdown.
![Xcode Command Line Tools](img/XcodeCommandLineTools.png)
<block class="mac linux windows android" />
@@ -158,20 +162,13 @@ Setting up your development environment can be somewhat tedious if you're new to
<block class="mac android" />
> Android Studio requires the [Java SE Development Kit(JDK)](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html), version 8. You can type `javac -version` in a terminal to see what version you have, if any.
```
$ javac -version
javac 1.8.0_111
```
> The version string `1.8.x_xxx` corresponds to JDK 8.
> Android Studio requires a recent version of the [Java SE Development Kit (JDK)](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html).
<block class="mac windows android" />
#### 2. Install the AVD and HAXM
Choose `Custom` installation when running Android Studio for the first time. Make sure the boxes next to all of the following are checked:
Android Virtual Devices allow you to run Android apps on your computer without the need for an actual Android phone or tablet. Choose `Custom` installation when running Android Studio for the first time. Make sure the boxes next to all of the following are checked:
- `Android SDK`
- `Android SDK Platform`
@@ -186,7 +183,7 @@ Then, click "Next" to install all of these components.
#### 2. Install the AVD and configure VM acceleration
Choose `Custom` installation when running Android Studio for the first time. Make sure the boxes next to all of the following are checked:
Android Virtual Devices allow you to run Android apps on your computer without the need for an actual Android phone or tablet. Choose `Custom` installation when running Android Studio for the first time. Make sure the boxes next to all of the following are checked:
- `Android SDK`
- `Android SDK Platform`
@@ -206,11 +203,12 @@ Select "SDK Platforms" from within the SDK Manager, then check the box next to "
- `Google APIs`
- `Android SDK Platform 23`
- `Intel x86 Atom System Image`
- `Intel x86 Atom_64 System Image`
- `Google APIs Intel x86 Atom_64 System Image`
Next, select "SDK Tools" and check the box next to "Show Package Details" here as well. Look for and expand the "Android SDK Build Tools" entry, then make sure that `Android SDK Build-Tools 23.0.1` is selected.
![Android SDK Manager](img/AndroidSDKManager.png)
Next, select the "SDK Tools" tab and check the box next to "Show Package Details" here as well. Look for and expand the "Android SDK Build Tools" entry, then make sure that `Android SDK Build-Tools 23.0.1` is selected.
Finally, click "Apply" to download and install the Android SDK and related build tools.
@@ -222,7 +220,7 @@ The React Native command line interface requires the `ANDROID_HOME` environment
<block class="mac android" />
Add the following lines to your `~/.bashrc` (or equivalent) config file:
Add the following lines to your `~/.profile` (or equivalent) config file:
```
export ANDROID_HOME=${HOME}/Library/Android/sdk
@@ -230,11 +228,13 @@ export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platform-tools
```
Type `source ~/.profile` to load the config into your current shell.
> Please make sure you export the correct path for `ANDROID_HOME`. If you installed the Android SDK using Homebrew, it would be located at `/usr/local/opt/android-sdk`.
<block class="linux android" />
Add the following lines to your `~/.bashrc` (or equivalent) config file:
Add the following lines to your `~/.profile` (or equivalent) config file:
```
export ANDROID_HOME=${HOME}/Android/Sdk
@@ -242,6 +242,8 @@ export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platform-tools
```
Type `source ~/.profile` to load the config into your current shell.
> Please make sure you export the correct path for `ANDROID_HOME` if you did not install the Android SDK using Android Studio.
<block class="windows android" />
@@ -276,9 +278,15 @@ You can see the list of available AVDs by opening the "AVD Manager" from within
android avd
```
Once in the "AVD Manager", select your AVD and click "Start...".
Once in the "AVD Manager", select your AVD and click "Edit...". Choose "Android 6.0 - API Level 23" under Device, and "Intel Atom (x86_64)" under CPU/ABI. Click OK, then select your new AVD and click "Start...", and finally, "Launch".
> Android Studio should have set up an Android Virtual Device for you during installation, but it is very common to run into an issue where Android Studio fails to install the AVD. You may follow the [Android Studio User Guide](https://developer.android.com/studio/run/managing-avds.html) to create a new AVD manually if needed.
![Android AVD Configuration](img/AndroidAVDConfiguration.png)
> It is very common to run into an issue where Android Studio fails to create a default AVD. You may follow the [Android Studio User Guide](https://developer.android.com/studio/run/managing-avds.html) to create a new AVD manually if needed.
### Using a real device
If you have a physical Android device, you can use it for development in place of an AVD. Plug it in to your computer using a USB cable and [enable USB debugging](https://developer.android.com/training/basics/firstapp/running-app.html) before proceeding to the next step.
<block class="mac ios android" />
@@ -296,6 +304,8 @@ react-native run-ios
You should see your new app running in the iOS Simulator shortly.
![AwesomeProject on iOS](img/iOSSuccess.png)
`react-native run-ios` is just one way to run your app. You can also run it directly from within Xcode or [Nuclide](https://nuclide.io/).
<block class="mac android" />
@@ -308,7 +318,9 @@ cd AwesomeProject
react-native run-android
```
If everything is set up correctly, you should see your new app running in your AVD shortly.
If everything is set up correctly, you should see your new app running in your Android emulator shortly.
![AwesomeProject on Android](img/AndroidSuccess.png)
`react-native run-android` is just one way to run your app - you can also run it directly from within Android Studio or [Nuclide](https://nuclide.io/).
@@ -348,7 +360,7 @@ cd AwesomeProject
react-native start
```
Open a new command prompt and run `react-native run-android` inside the same folder to launch the app on your AVD.
Open a new command prompt and run `react-native run-android` inside the same folder to launch the app on your Android emulator.
```
react-native run-android
@@ -370,11 +382,7 @@ react-native run-android
If everything is set up correctly, you should see your new app running in your Android emulator shortly.
<block class="windows android" />
> If you're targeting API level 23, the app might crash on first launch with an error smilar to `Unable to add window android.view.ViewRootImpl$W@c51fa6 -- permission denied for this window type`. To fix this, you need to go to `System settings > Apps > Configure apps > Draw over other apps` and grant the permission for the app.
NOTE: Many React Native modules haven't been tested on Marshmallow and might break. Please thoroughly test the app if you target API level 23 and file a bug report if you find that something is broken.
![AwesomeProject on Android](img/AndroidSuccess.png)
<block class="windows linux android" />
+1 -1
View File
@@ -5,7 +5,7 @@ layout: docs
category: Guides
permalink: docs/handling-touches.html
next: animations
previous: images
previous: more-resources
---
Users interact with mobile apps mainly through touch. They can use a combination of gestures, such as tapping on a button, scrolling a list, or zooming on a map.
+1
View File
@@ -4,6 +4,7 @@ title: Headless JS
layout: docs
category: Guides (Android)
permalink: docs/headless-js-android.html
banner: ejected
next: signed-apk-android
previous: native-components-android
---
+2 -2
View File
@@ -28,7 +28,7 @@ class FixedDimensionsBasics extends Component {
</View>
);
}
};
}
AppRegistry.registerComponent('AwesomeProject', () => FixedDimensionsBasics);
```
@@ -58,7 +58,7 @@ class FlexDimensionsBasics extends Component {
</View>
);
}
};
}
AppRegistry.registerComponent('AwesomeProject', () => FlexDimensionsBasics);
```
+2 -2
View File
@@ -4,8 +4,8 @@ title: Images
layout: docs
category: Guides
permalink: docs/images.html
next: handling-touches
previous: colors
next: colors
previous: navigation
---
## Static Image Resources
+3 -2
View File
@@ -4,8 +4,9 @@ title: Integration With Existing Apps
layout: docs
category: Guides
permalink: docs/integration-with-existing-apps.html
next: colors
previous: more-resources
banner: ejected
next: running-on-device
previous: testing
---
<div class="integration-toggler">
+1 -1
View File
@@ -34,7 +34,7 @@ This especially includes **new modules** Facebook doesn't use in production. Exp
### An issue is a feature request, you're pretty sure we should maintain this feature as part of RN
This should be rare - adding a new feature means maintaining it.
Tell the author something like: "Pull requests are welcome. In case you're not up for sending a PR, you should post to [Product Pains](https://productpains.com/product/react-native/?tab=top). It has a voting system and if the feature gets upvoted enough it might get implemented."
Tell the author something like: "Pull requests are welcome. In case you're not up for sending a PR, you should post to [Canny](https://react-native.canny.io/feature-requests). It has a voting system and if the feature gets upvoted enough it might get implemented."
### How to add a label
Add any relevant labels, for example 'Android', 'iOS'.
+2 -2
View File
@@ -4,8 +4,8 @@ title: JavaScript Environment
layout: docs
category: Guides
permalink: docs/javascript-environment.html
next: navigation
previous: testing
next: direct-manipulation
previous: timers
---
## JavaScript Runtime
+1
View File
@@ -4,6 +4,7 @@ title: Linking Libraries
layout: docs
category: Guides (iOS)
permalink: docs/linking-libraries-ios.html
banner: ejected
next: running-on-simulator-ios
previous: native-components-ios
---
+3 -3
View File
@@ -4,8 +4,8 @@ title: More Resources
layout: docs
category: The Basics
permalink: docs/more-resources.html
next: integration-with-existing-apps
previous: using-navigators
next: handling-touches
previous: networking
---
If you just read through this website, you should be able to build a pretty cool React Native app. But React Native isn't just a product made by one company - it's a community of thousands of developers. So if you're interested in React Native, here's some related stuff you might want to check out.
@@ -32,7 +32,7 @@ The folks who built the app for Facebook's F8 conference in 2016 also [open-sour
[CodePush](https://microsoft.github.io/code-push/) is a service from Microsoft that makes it easy to deploy live updates to your React Native app. If you don't like going through the app store process to deploy little tweaks, and you also don't like setting up your own backend, give CodePush a try.
[Exponent](http://docs.getexponent.com/versions/v6.0.0/index.html) is a development environment plus application that focuses on letting you build React Native apps in the Exponent development environment, without ever touching Xcode or Android Studio. If you wish React Native was even more JavaScripty and webby, check out Exponent.
[Expo](https://docs.expo.io) is a development environment plus application that focuses on letting you build React Native apps in the Expo development environment, without ever touching Xcode or Android Studio. If you wish React Native was even more JavaScripty and webby, check out Expo.
[Deco](https://www.decosoftware.com/) is an all-in-one development environment specifically designed for React Native. It can automatically set up a new project, search for open source components, and insert them. You can also tweak your app graphically in real time. Check it out if you use macOS.
+1
View File
@@ -4,6 +4,7 @@ title: Native UI Components
layout: docs
category: Guides (Android)
permalink: docs/native-components-android.html
banner: ejected
next: headless-js-android
previous: native-modules-android
---
+1
View File
@@ -4,6 +4,7 @@ title: Native UI Components
layout: docs
category: Guides (iOS)
permalink: docs/native-components-ios.html
banner: ejected
next: linking-libraries-ios
previous: native-modules-ios
---
+4 -3
View File
@@ -4,6 +4,7 @@ title: Native Modules
layout: docs
category: Guides (Android)
permalink: docs/native-modules-android.html
banner: ejected
next: native-components-android
previous: communication-ios
---
@@ -131,7 +132,7 @@ public class AnExampleReactPackage implements ReactPackage {
return modules;
}
}
```
@@ -369,9 +370,9 @@ public class ImagePickerModule extends ReactContextBaseJavaModule {
private static final String E_NO_IMAGE_DATA_FOUND = "E_NO_IMAGE_DATA_FOUND";
private Promise mPickerPromise;
private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
if (requestCode == IMAGE_PICKER_REQUEST) {
+4 -3
View File
@@ -4,8 +4,9 @@ title: Native Modules
layout: docs
category: Guides (iOS)
permalink: docs/native-modules-ios.html
banner: ejected
next: native-components-ios
previous: gesture-responder-system
previous: upgrading
---
Sometimes an app needs access to platform API, and React Native doesn't have a corresponding module yet. Maybe you want to reuse some existing Objective-C, Swift or C++ code without having to reimplement it in JavaScript, or write some high performance, multi-threaded code such as for image processing, a database, or any number of advanced extensions.
@@ -398,13 +399,13 @@ You will receive a warning if you expend resources unnecessarily by emitting an
}
// Will be called when this module's first listener is added.
-(void)startObserving {
-(void)startObserving {
hasListeners = YES;
// Set up any upstream listeners or background tasks as necessary
}
// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
-(void)stopObserving {
hasListeners = NO;
// Remove upstream listeners, stop unnecessary background tasks
}
+62 -252
View File
@@ -4,19 +4,71 @@ title: Navigation
layout: docs
category: Guides
permalink: docs/navigation.html
next: performance
previous: javascript-environment
next: images
previous: animations
---
This guide covers the various navigation components available in React Native. If you are just getting started with navigation, you will probably want to use `Navigator`. If you are only targeting iOS and would like to stick to the native look and feel, check out `NavigatorIOS`. If you are looking for greater control over your navigation stack, you can't go wrong with `NavigationExperimental`.
This guide covers the various navigation components available in React Native. If you are just getting started with navigation, you will probably want to use React Navigation.
If you are only targeting iOS and would like to stick to the native look and feel, check out `NavigatorIOS`. The `Navigator` component is older but has been thoroughly tested in production.
## React Navigation
The community solution to navigation is a standalone library that allows developers to set up the screens of an app with just a few lines of code.
The first step is to install in your app:
```
npm install --save react-navigation
```
Then you can quickly create an app with a home screen and a profile screen:
```
import {
StackNavigator,
} from 'react-navigation';
const App = StackNavigator({
Main: {screen: MainScreen},
Profile: {screen: ProfileScreen},
});
```
Each screen component can set navigation options such as the header title. It can use action creators on the `navigation` prop to link to other screens:
```
class MainScreen extends React.Component {
static navigationOptions = {
title: 'Welcome',
};
render() {
const { navigate } = this.props.navigation;
return (
<Button
title="Go to Jane's profile"
onPress={() =>
navigate('Profile', { name: 'Jane' });
}
/>
);
}
}
```
React Navigation routers make it easy to override navigation logic or integrate it into redux. Because routers can be nested inside eachother, developers can override navigation logic for one area of the app without making widespread changes.
The views in React Navigation use native components and the `Animated` library to deliver 60fps animations that are run on the native thread. Plus, the animations and gestures can be easily customized.
For a complete intro to React Navigation, follow the [getting started guide](https://reactnavigation.org/docs/intro/), or browse other docs such as the [intro to navigators](https://reactnavigation.org/docs/navigators/).
## Navigator
`Navigator` provides a JavaScript implementation of a navigation stack, so it works on both iOS and Android and is easy to customize. This is the same component you used to build your first navigation stack in the [navigators tutorial](docs/using-navigators.html).
Like React Navigation, `Navigator` provides a JavaScript implementation of a navigation stack, so it works on both iOS and Android and is easy to customize. Navigator was released alongside React Native in 2015, so it predates the Animated library with native-thread animations.
![](img/NavigationStack-Navigator.gif)
`Navigator` can easily be adapted to render different components based on the current route in its `renderScene` function. It will transition new scenes onto the screen by sliding in from the right by default, but you can control this behavior by using the `configureScene` function. You can also configure a navigation bar through the `navigationBar` prop.
`Navigator` can be adapted to render different components based on the current route in its `renderScene` function. It will transition new scenes onto the screen by sliding in from the right by default, but you can control this behavior by using the `configureScene` function. You can also configure a navigation bar through the `navigationBar` prop.
Check out the [Navigator API reference](docs/navigator.html) for specific examples that cover each of these scenarios.
@@ -36,7 +88,7 @@ If you are targeting iOS only, you may also want to consider using [NavigatorIOS
/>
```
Just like `Navigator`, `NavigatorIOS` uses routes to represent scenes, with some important differences. The actual component that will be rendered can be specified using the `component` key in the route, and any props that should be passed to this component can be specified in `passProps`. A "navigator" object is automatically passed as a prop to the component, allowing you to call `push` and `pop` as needed.
Like other navigation systems, `NavigatorIOS` uses routes to represent screens, with some important differences. The actual component that will be rendered can be specified using the `component` key in the route, and any props that should be passed to this component can be specified in `passProps`. A "navigator" object is automatically passed as a prop to the component, allowing you to call `push` and `pop` as needed.
As `NavigatorIOS` leverages native UIKit navigation, it will automatically render a navigation bar with a back button and title.
@@ -94,252 +146,10 @@ Check out the [`NavigatorIOS` reference docs](docs/navigatorios.html) to learn m
## NavigationExperimental
`Navigator` and `NavigatorIOS` are both stateful components. If your app has multiple of these, it can become tricky to coordinate navigation transitions between them. NavigationExperimental provides a different approach to navigation, allowing any view to act as a navigation view and using reducers to manipulate state at a top-level object. It is bleeding edge as the name implies, but you might want to check it out if you are craving greater control over your app's navigation.
Since early 2016, React Native has shipped with an experimental re-implementation of the original `Navigator` component called `CardStack`. The major benefit it had over `Navigator` is the smooth native-thread animations provided by the Animated library.
```javascript
<NavigationCardStack
onNavigateBack={onPopRouteFunc}
navigationState={myNavigationState}
renderScene={renderSceneFun}
/>
```
Because `NavigationExperimental` only included view components, it required a lot of boilerplate to use by itself. Several libraries sprung up around it, making it easier to use. Libraries such as `react-native-router-flux` and `ex-navigation` wrapped NavigationExperimental views in an easier-to-use API. Authors of many of these libraries now support React Navigation.
You can import `NavigationExperimental` like any other component in React Native. Once you have that, you can deconstruct any additional components from `NavigationExperimental` that you may find useful. Since I am feeling like building navigation stacks today, I'll go ahead and pick out `NavigationCardStack` and `NavigationStateUtils`.
The `CardStack` and other NavigationExperimental views live on as a part of the React Navigation project. The new library aims to be easy to use, while continuing to enable the smooth and customizable animations that NavigationExperimental pioneered.
```javascript
import React, { Component } from 'react';
import { NavigationExperimental } from 'react-native';
const {
CardStack: NavigationCardStack,
StateUtils: NavigationStateUtils,
} = NavigationExperimental;
```
As I said earlier, `NavigationExperimental` takes a different approach than `Navigator` and `NavigatorIOS`. Using it to build a navigation stack requires a few more steps than the stateful components, but the payoff is worth it.
### Step 1. Define Initial State and Top Level Component
Create a new component for your application. This will be the top-level object, so we will define the initial state here. The navigation state will be defined in the `navigationState` key, where we define our initial route:
```javascript
class BleedingEdgeApplication extends Component {
constructor(props, context) {
super(props, context);
this.state = {
// This defines the initial navigation state.
navigationState: {
index: 0, // Starts with first route focused.
routes: [{key: 'My Initial Scene'}], // Starts with only one route.
},
};
// We'll define this function later - hang on
this._onNavigationChange = this._onNavigationChange.bind(this);
}
_onNavigationChange(type) {
// It's literally the next step. We'll get to it!
}
render() {
return (
<Text>This is a placeholder. We will come back to this and render our navigation here later.</Text>
);
}
}
```
Alright, now we have a simple stateful component that doesn't do much at all. We can change that. Our initial state contains one route, and the current index. That looks suspiciously just like our initial route definition in Navigator. Do you remember which actions its navigator object provided?
Push and pop, of course. That seems pretty straightforward to implement. I promised you earlier we would be using reducers to manage state at the top-level object. Sit tight.
### Step 2. Reducing the Navigation State
NavigationExperimental comes built-in with some useful reducers, and they are all available as part of NavigationStateUtils. The two we will be using right now are called -- yep -- push and pop. They take a navigationState object, and return a new navigationState object.
We can use them to write our `_onNavigationChange` function which, given a "push" or "pop" action, will reduce the state accordingly.
```javascript
_onNavigationChange(type) {
// Extract the navigationState from the current state:
let {navigationState} = this.state;
switch (type) {
case 'push':
// Push a new route, which in our case is an object with a key value.
// I am fond of cryptic keys (but seriously, keys should be unique)
const route = {key: 'Route-' + Date.now()};
// Use the push reducer provided by NavigationStateUtils
navigationState = NavigationStateUtils.push(navigationState, route);
break;
case 'pop':
// Pop the current route using the pop reducer.
navigationState = NavigationStateUtils.pop(navigationState);
break;
}
// NavigationStateUtils gives you back the same `navigationState` if nothing
// has changed. We will only update state if it has changed.
if (this.state.navigationState !== navigationState) {
// Always use setState() when setting a new state!
this.setState({navigationState});
// If you are new to ES6, the above is equivalent to:
// this.setState({navigationState: navigationState});
}
}
```
Cool. I'm getting the hang of this. This is the heart of NavigationExperimental. We are only handling two actions here, but a more complex application could also take into account a "back" action (e.g. Android back button), as well as handle the transition between several tabs in a tabbed application.
I am still missing the initial scene that will be rendered (as well as the actual navigator that will wrap it, but let's not get ahead of ourselves).
### Step 3. Define Scenes
First I want to define a Row component out of convenience. It displays some text and can call some function when pressed.
```javascript
class TappableRow extends Component {
render() {
return (
<TouchableHighlight
style={styles.row}
underlayColor="#D0D0D0"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>
{this.props.text}
</Text>
</TouchableHighlight>
);
}
}
```
Now I will define my actual scene. It uses a scroll view to display a vertical list of items. The first row displays the current route's key, and two more rows will call our theoretical navigator's push and pop functions.
```javascript
class MyVeryComplexScene extends Component {
render() {
return (
<ScrollView style={styles.scrollView}>
<Text style={styles.row}>
Route: {this.props.route.key}
</Text>
<TappableRow
text="Tap me to load the next scene"
onPress={this.props.onPushRoute}
/>
<TappableRow
text="Tap me to go back"
onPress={this.props.onPopRoute}
/>
</ScrollView>
);
}
}
```
### Step 4. Create a Navigation Stack
Now that I have defined the state and a function to manage it, I think I can go ahead and create a proper navigator component now. While I'm at it, I'll render my scene after configuring it with the current route's props.
```javascript
class MyVerySimpleNavigator extends Component {
// This sets up the methods (e.g. Pop, Push) for navigation.
constructor(props, context) {
super(props, context);
this._onPushRoute = this.props.onNavigationChange.bind(null, 'push');
this._onPopRoute = this.props.onNavigationChange.bind(null, 'pop');
this._renderScene = this._renderScene.bind(this);
}
// Now we finally get to use the `NavigationCardStack` to render the scenes.
render() {
return (
<NavigationCardStack
onNavigateBack={this._onPopRoute}
navigationState={this.props.navigationState}
renderScene={this._renderScene}
style={styles.navigator}
/>
);
}
// Render a scene for route.
// The detailed spec of `sceneProps` is defined at `NavigationTypeDefinition`
// as type `NavigationSceneRendererProps`.
// Here you could choose to render a different component for each route, but
// we'll keep it simple.
_renderScene(sceneProps) {
return (
<MyVeryComplexScene
route={sceneProps.scene.route}
onPushRoute={this._onPushRoute}
onPopRoute={this._onPopRoute}
onExit={this.props.onExit}
/>
);
}
}
```
That's it -- so close to the finish line I can smell it. Let's plug our new navigator into our top-level component:
```javascript
class BleedingEdgeApplication extends Component {
// constructor and other methods omitted for clarity
render() {
return (
<MyVerySimpleNavigator
navigationState={this.state.navigationState}
onNavigationChange={this._onNavigationChange}
onExit={this._exit}
/>
);
}
}
```
We're done! Bask in the glory of NavigationExperimental.
#### Hey -- I think you are missing something.
(Oh yes, sorry about that -- here's our missing imports and styles.)
```javascript
import { NavigationExperimental, PixelRatio, ScrollView, StyleSheet, Text, TouchableHighlight } from 'react-native';
const styles = StyleSheet.create({
navigator: {
flex: 1,
},
scrollView: {
marginTop: 64
},
row: {
padding: 15,
backgroundColor: 'white',
borderBottomWidth: 1 / PixelRatio.get(),
borderBottomColor: '#CDCDCD',
},
rowText: {
fontSize: 17,
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
});
```
### Homework
You are now an expert navigator. Take a look at [NavigationExperimental in UIExplorer](https://github.com/facebook/react-native/tree/master/Examples/UIExplorer/js/NavigationExperimental) to learn how to implement other types of navigation hierarchies, such as a tabbed application with multiple navigation stacks.
As of React Native 0.43, `NavigationExperimental` is deprecated. It will be removed from the codebase in a later version.
+1 -1
View File
@@ -4,7 +4,7 @@ title: Networking
layout: docs
category: The Basics
permalink: docs/network.html
next: using-navigators
next: more-resources
previous: using-a-listview
---
+279 -268
View File
@@ -4,308 +4,125 @@ title: Performance
layout: docs
category: Guides
permalink: docs/performance.html
next: understanding-cli
previous: navigation
next: gesture-responder-system
previous: direct-manipulation
---
A compelling reason for using React Native instead of WebView-based
tools is to achieve 60 FPS and a native look & feel to your apps. Where
possible, we would like for React Native to do the right thing and help
you to focus on your app instead of performance optimization, but there
are areas where we're not quite there yet, and others where React Native
(similar to writing native code directly) cannot possibly determine the
best way to optimize for you and so manual intervention will be
necessary.
A compelling reason for using React Native instead of WebView-based tools is to achieve 60 frames per second and a native look and feel to your apps.
Where possible, we would like for React Native to do the right thing and help you to focus on your app instead of performance optimization,
but there are areas where we're not quite there yet,
and others where React Native (similar to writing native code directly) cannot possibly determine the best way to optimize for you and so manual intervention will be necessary.
We try our best to deliver buttery-smooth UI performance by default, but sometimes that just isn't possible.
This guide is intended to teach you some basics to help you
to troubleshoot performance issues, as well as discuss common sources of
problems and their suggested solutions.
This guide is intended to teach you some basics to help you to [troubleshoot performance issues](docs/performance.html#profiling),
as well as discuss [common sources of problems and their suggested solutions](docs/performance.html#common-sources-of-performance-problems).
### What you need to know about frames
## What you need to know about frames
Your grandparents' generation called movies ["moving
pictures"](https://www.youtube.com/watch?v=F1i40rnpOsA) for a reason:
realistic motion in video is an illusion created by quickly changing
static images at a consistent speed. We refer to each of these images as
frames. The number of frames that is displayed each second has a direct
impact on how smooth and ultimately life-like a video (or user
interface) seems to be. iOS devices display 60 frames per second, which
gives you and the UI system about 16.67ms to do all of the work needed to
generate the static image (frame) that the user will see on the screen
for that interval. If you are unable to do the work necessary to
generate that frame within the allotted 16.67ms, then you will "drop a
frame" and the UI will appear unresponsive.
Your grandparents' generation called movies ["moving pictures"](https://www.youtube.com/watch?v=F1i40rnpOsA) for a reason:
realistic motion in video is an illusion created by quickly changing static images at a consistent speed. We refer to each of these images as frames.
The number of frames that is displayed each second has a direct impact on how smooth and ultimately life-like a video (or user interface) seems to be.
iOS devices display 60 frames per second, which gives you and the UI system about 16.67ms to do all of the work needed to generate the static image (frame) that the user will see on the screen for that interval.
If you are unable to do the work necessary to generate that frame within the allotted 16.67ms, then you will "drop a frame" and the UI will appear unresponsive.
Now to confuse the matter a little bit, open up the developer menu in
your app and toggle `Show Perf Monitor`. You will notice that there are
two different frame rates.
Now to confuse the matter a little bit, open up the developer menu in your app and toggle `Show Perf Monitor`.
You will notice that there are two different frame rates.
#### JavaScript frame rate
![](img/PerfUtil.png)
For most React Native applications, your business logic will run on the
JavaScript thread. This is where your React application lives, API calls
are made, touch events are processed, etc... Updates to native-backed
views are batched and sent over to the native side at the end of each iteration of the event loop, before the frame deadline (if
all goes well). If the JavaScript thread is unresponsive for a frame, it
will be considered a dropped frame. For example, if you were to call
`this.setState` on the root component of a complex application and it
resulted in re-rendering computationally expensive component subtrees,
it's conceivable that this might take 200ms and result in 12 frames
being dropped. Any animations controlled by JavaScript would appear to freeze during that time. If anything takes longer than 100ms, the user will feel it.
### JS frame rate (JavaScript thread)
This often happens during Navigator transitions: when you push a new
route, the JavaScript thread needs to render all of the components
necessary for the scene in order to send over the proper commands to the
native side to create the backing views. It's common for the work being
done here to take a few frames and cause jank because the transition is
controlled by the JavaScript thread. Sometimes components will do
additional work on `componentDidMount`, which might result in a second
stutter in the transition.
For most React Native applications, your business logic will run on the JavaScript thread.
This is where your React application lives, API calls are made, touch events are processed, etc...
Updates to native-backed views are batched and sent over to the native side at the end of each iteration of the event loop,
before the frame deadline (if all goes well).
If the JavaScript thread is unresponsive for a frame, it will be considered a dropped frame.
For example, if you were to call `this.setState` on the root component of a complex application and it resulted in re-rendering computationally expensive component subtrees,
it's conceivable that this might take 200ms and result in 12 frames being dropped.
Any animations controlled by JavaScript would appear to freeze during that time.
If anything takes longer than 100ms, the user will feel it.
Another example is responding to touches: if you are doing work across
multiple frames on the JavaScript thread, you might notice a delay in
responding to TouchableOpacity, for example. This is because the JavaScript thread is busy and cannot process the raw touch events sent over from the main thread. As a result, TouchableOpacity cannot react to the touch events and command the native view to adjust its opacity.
This often happens during `Navigator` transitions:
when you push a new route, the JavaScript thread needs to render all of the components necessary for the scene in order to send over the proper commands to the native side to create the backing views.
It's common for the work being done here to take a few frames and cause [jank](http://jankfree.org/) because the transition is controlled by the JavaScript thread.
Sometimes components will do additional work on `componentDidMount`, which might result in a second stutter in the transition.
#### Main thread (aka UI thread) frame rate
Another example is responding to touches:
if you are doing work across multiple frames on the JavaScript thread, you might notice a delay in responding to `TouchableOpacity`, for example.
This is because the JavaScript thread is busy and cannot process the raw touch events sent over from the main thread.
As a result, `TouchableOpacity` cannot react to the touch events and command the native view to adjust its opacity.
Many people have noticed that performance of `NavigatorIOS` is better
out of the box than `Navigator`. The reason for this is that the
animations for the transitions are done entirely on the main thread, and
so they are not interrupted by frame drops on the JavaScript thread.
([Read about why you should probably use Navigator
anyways.](docs/using-navigators.html)
### UI frame rate (main thread)
Similarly, you can happily scroll up and down through a ScrollView when
the JavaScript thread is locked up because the ScrollView lives on the
main thread (the scroll events are dispatched to the JS thread though,
but their receipt is not necessary for the scroll to occur).
Many people have noticed that performance of `NavigatorIOS` is better out of the box than `Navigator`.
The reason for this is that the animations for the transitions are done entirely on the main thread,
and so they are not interrupted by frame drops on the JavaScript thread.
### Common sources of performance problems
Similarly, you can happily scroll up and down through a `ScrollView` when the JavaScript thread is locked up because the `ScrollView` lives on the main thread.
The scroll events are dispatched to the JS thread, but their receipt is not necessary for the scroll to occur.
#### Console.log statements
## Common sources of performance problems
When running a bundled app, these statements can cause a big bottleneck in the JavaScript thread. This includes calls from debugging libraries such as [redux-logger](https://github.com/evgenyrodionov/redux-logger), so make sure to remove them before bundling.
> There is a [babel plugin](https://babeljs.io/docs/plugins/transform-remove-console/) that can remove all `console.*` calls. You need to install it first using `npm install babel-plugin-transform-remove-console --save`, and then edit (or create) `.babelrc` under your project directory like the following:
```json
{
"env": {
"production": {
"plugins": ["transform-remove-console"]
}
}
}
```
Then it will automatically remove all `console.*` calls in a release (production) version of your project. However, the `console.*` calls will still be executed in the debug version of your project.
#### Development mode (dev=true)
### Running in development mode (`dev=true`)
JavaScript thread performance suffers greatly when running in dev mode.
This is unavoidable: a lot more work needs to be done at runtime to
provide you with good warnings and error messages, such as validating
propTypes and various other assertions.
This is unavoidable: a lot more work needs to be done at runtime to provide you with good warnings and error messages, such as validating propTypes and various other assertions.
#### Slow navigator transitions
### Using `console.log` statements
As mentioned above, `Navigator` animations are controlled by the
JavaScript thread. Imagine the "push from right" scene transition: each
frame, the new scene is moved from the right to left, starting offscreen
(let's say at an x-offset of 320) and ultimately settling when the scene sits
at an x-offset of 0. Each frame during this transition, the
JavaScript thread needs to send a new x-offset to the main thread.
If the JavaScript thread is locked up, it cannot do this and so no
update occurs on that frame and the animation stutters.
When running a bundled app, these statements can cause a big bottleneck in the JavaScript thread.
This includes calls from debugging libraries such as [redux-logger](https://github.com/evgenyrodionov/redux-logger),
so make sure to remove them before bundling.
Part of the long-term solution to this is to allow for JavaScript-based
animations to be offloaded to the main thread. If we were to do the same
thing as in the above example with this approach, we might calculate a
list of all x-offsets for the new scene when we are starting the
transition and send them to the main thread to execute in an
optimized way. Now that the JavaScript thread is freed of this
responsibility, it's not a big deal if it drops a few frames while
rendering the scene -- you probably won't even notice because you will be
too distracted by the pretty transition.
### `ListView` initial rendering is too slow or scroll performance is bad for large lists
Unfortunately this solution is not yet implemented, and so in the
meantime we should use the InteractionManager to selectively render the
minimal amount of content necessary for the new scene as long as the
animation is in progress. `InteractionManager.runAfterInteractions` takes
a callback as its only argument, and that callback is fired when the
navigator transition is complete (each animation from the `Animated` API
also notifies the InteractionManager, but that's beyond the scope of
this discussion).
Use the new [`FlatList`](docs/flatlist.html) or [`SectionList`](docs/sectionlist.html) component instead.
Besides simplifying the API, the new list components also have significant performance enhancements,
the main one being nearly constant memory usage for any number of rows.
Your scene component might look something like this:
TODO: Link to blog post
```js
class ExpensiveScene extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {renderPlaceholderOnly: true};
}
### JS FPS plunges when re-rendering a view that hardly changes
componentDidMount() {
InteractionManager.runAfterInteractions(() => {
this.setState({renderPlaceholderOnly: false});
});
}
If you are using a ListView, you must provide a `rowHasChanged` function that can reduce a lot of work by quickly determining whether or not a row needs to be re-rendered. If you are using immutable data structures, this would be as simple as a reference equality check.
render() {
if (this.state.renderPlaceholderOnly) {
return this._renderPlaceholderView();
}
Similarly, you can implement `shouldComponentUpdate` and indicate the exact conditions under which you would like the component to re-render. If you write pure components (where the return value of the render function is entirely dependent on props and state), you can leverage PureRenderMixin to do this for you. Once again, immutable data structures are useful to keep this fast -- if you have to do a deep comparison of a large list of objects, it may be that re-rendering your entire component would be quicker, and it would certainly require less code.
return (
<View>
<Text>Your full view goes here</Text>
</View>
);
}
### Dropping JS thread FPS because of doing a lot of work on the JavaScript thread at the same time
"Slow Navigator transitions" is the most common manifestation of this, but there are other times this can happen. Using InteractionManager can be a good approach, but if the user experience cost is too high to delay work during an animation, then you might want to consider LayoutAnimation.
_renderPlaceholderView() {
return (
<View>
<Text>Loading...</Text>
</View>
);
}
};
```
The Animated api currently calculates each keyframe on-demand on the JavaScript thread, while LayoutAnimation leverages Core Animation and is unaffected by JS thread and main thread frame drops.
You don't need to be limited to rendering some loading indicator, you
could alternatively render part of your content -- for example, when you
load the Facebook app you see a placeholder news feed item with grey
rectangles where text will be. If you are rendering a Map in your new
scene, you might want to display a grey placeholder view or a spinner
until the transition is complete as this can actually cause frames to be
dropped on the main thread.
#### ListView initial rendering is too slow or scroll performance is bad for large lists
This is an issue that comes up frequently because iOS ships with
UITableView which gives you very good performance by re-using underlying
UIViews. Work is in progress to do something similar with React Native,
but until then we have some tools at our disposal to help us tweak the
performance to suit our needs. It may not be possible to get all the way
there, but a little bit of creativity and experimentation with these
options can go a long way.
##### initialListSize
This prop specifies how many rows we want to render on our first render
pass. If we are concerned with getting *something* on screen as quickly
as possible, we could set the `initialListSize` to 1, and we'll quickly
see other rows fill in on subsequent frames. The number of rows per
frame is determined by the `pageSize`.
##### pageSize
After the initial render where `initialListSize` is used, ListView looks
at the `pageSize` to determine how many rows to render per frame. The
default here is 1 -- but if your views are very small and inexpensive to
render, you might want to bump this up. Tweak it and find what works for
your use case.
##### scrollRenderAheadDistance
"How early to start rendering rows before they come on screen, in pixels."
If we had a list with 2000 items and rendered them all immediately that
would be a poor use of both memory and computational resources. It would
also probably cause some pretty awful jank. So the scrollRenderAhead
distance allows us to specify how far beyond the current viewport we
should continue to render rows.
##### removeClippedSubviews
"When true, offscreen child views (whose `overflow` value is `hidden`)
are removed from their native backing superview when offscreen. This
can improve scrolling performance on long lists. The default value is
`true`."(The default value is `false` before version 0.14-rc).
This is an extremely important optimization to apply on large ListViews.
On Android the `overflow` value is always `hidden` so you don't need to
worry about setting it, but on iOS you need to be sure to set `overflow:
hidden` on row containers.
#### My component renders too slowly and I don't need it all immediately
It's common at first to overlook ListView, but using it properly is
often key to achieving solid performance. As discussed above, it
provides you with a set of tools that lets you split rendering of your
view across various frames and tweak that behavior to fit your specific
needs. Remember that ListView can be horizontal too.
#### JS FPS plunges when re-rendering a view that hardly changes
If you are using a ListView, you must provide a `rowHasChanged` function
that can reduce a lot of work by quickly determining whether or not a
row needs to be re-rendered. If you are using immutable data structures,
this would be as simple as a reference equality check.
Similarly, you can implement `shouldComponentUpdate` and indicate the
exact conditions under which you would like the component to re-render.
If you write pure components (where the return value of the render
function is entirely dependent on props and state), you can leverage
PureRenderMixin to do this for you. Once again, immutable data
structures are useful to keep this fast -- if you have to do a deep
comparison of a large list of objects, it may be that re-rendering your
entire component would be quicker, and it would certainly require less
code.
#### Dropping JS thread FPS because of doing a lot of work on the JavaScript thread at the same time
"Slow Navigator transitions" is the most common manifestation of this,
but there are other times this can happen. Using InteractionManager can
be a good approach, but if the user experience cost is too high to delay
work during an animation, then you might want to consider
LayoutAnimation.
The Animated api currently calculates each keyframe on-demand on the
JavaScript thread, while LayoutAnimation leverages Core Animation and is
unaffected by JS thread and main thread frame drops.
One case where I have used this is for animating in a modal (sliding
down from top and fading in a translucent overlay) while
initializing and perhaps receiving responses for several network
requests, rendering the contents of the modal, and updating the view
where the modal was opened from. See the Animations guide for more
information about how to use LayoutAnimation.
One case where I have used this is for animating in a modal (sliding down from top and fading in a translucent overlay) while initializing and perhaps receiving responses for several network requests, rendering the contents of the modal, and updating the view where the modal was opened from. See the Animations guide for more information about how to use LayoutAnimation.
Caveats:
- LayoutAnimation only works for fire-and-forget animations ("static"
animations) -- if it must be be interruptible, you will need to use
Animated.
#### Moving a view on the screen (scrolling, translating, rotating) drops UI thread FPS
- LayoutAnimation only works for fire-and-forget animations ("static" animations) -- if it must be be interruptible, you will need to use `Animated`.
This is especially true when you have text with a transparent background
positioned on top of an image, or any other situation where alpha
compositing would be required to re-draw the view on each frame. You
will find that enabling `shouldRasterizeIOS` or `renderToHardwareTextureAndroid`
can help with this significantly.
### Moving a view on the screen (scrolling, translating, rotating) drops UI thread FPS
Be careful not to overuse this or your memory usage could go through the
roof. Profile your performance and memory usage when using these props. If you don't plan to move a view anymore, turn this property off.
This is especially true when you have text with a transparent background positioned on top of an image,
or any other situation where alpha compositing would be required to re-draw the view on each frame.
You will find that enabling `shouldRasterizeIOS` or `renderToHardwareTextureAndroid` can help with this significantly.
#### Animating the size of an image drops UI thread FPS
Be careful not to overuse this or your memory usage could go through the roof.
Profile your performance and memory usage when using these props.
If you don't plan to move a view anymore, turn this property off.
On iOS, each time you adjust the width or height of an Image component
it is re-cropped and scaled from the original image. This can be very expensive,
especially for large images. Instead, use the `transform: [{scale}]`
style property to animate the size. An example of when you might do this is
when you tap an image and zoom it in to full screen.
### Animating the size of an image drops UI thread FPS
#### My TouchableX view isn't very responsive
On iOS, each time you adjust the width or height of an Image component it is re-cropped and scaled from the original image.
This can be very expensive, especially for large images.
Instead, use the `transform: [{scale}]` style property to animate the size.
An example of when you might do this is when you tap an image and zoom it in to full screen.
Sometimes, if we do an action in the same frame that we are adjusting
the opacity or highlight of a component that is responding to a touch,
### My TouchableX view isn't very responsive
Sometimes, if we do an action in the same frame that we are adjusting the opacity or highlight of a component that is responding to a touch,
we won't see that effect until after the `onPress` function has returned.
If `onPress` does a `setState` that results in a lot of work and a few
frames dropped, this may occur. A solution to this is to wrap any action
inside of your `onPress` handler in `requestAnimationFrame`:
If `onPress` does a `setState` that results in a lot of work and a few frames dropped, this may occur.
A solution to this is to wrap any action inside of your `onPress` handler in `requestAnimationFrame`:
```js
handleOnPress() {
@@ -317,14 +134,208 @@ handleOnPress() {
}
```
### Profiling
### Slow navigator transitions
Use the built-in Profiler to get detailed information about work done in
the JavaScript thread and main thread side-by-side.
As mentioned above, `Navigator` animations are controlled by the JavaScript thread.
Imagine the "push from right" scene transition:
each frame, the new scene is moved from the right to left,
starting offscreen (let's say at an x-offset of 320) and ultimately settling when the scene sits at an x-offset of 0.
Each frame during this transition, the JavaScript thread needs to send a new x-offset to the main thread.
If the JavaScript thread is locked up, it cannot do this and so no update occurs on that frame and the animation stutters.
For iOS, Instruments are an invaluable tool, and on Android you should
learn to use systrace.
One solution to this is to allow for JavaScript-based animations to be offloaded to the main thread.
If we were to do the same thing as in the above example with this approach,
we might calculate a list of all x-offsets for the new scene when we are starting the transition and send them to the main thread to execute in an optimized way.
Now that the JavaScript thread is freed of this responsibility,
it's not a big deal if it drops a few frames while rendering the scene -- you probably won't even notice because you will be too distracted by the pretty transition.
Solving this is one of the main goals behind the new [React Navigation](docs/navigation.html) library.
The views in React Navigation use native components and the [`Animated`](docs/animated.html) library to deliver 60 FPS animations that are run on the native thread.
## Profiling
Use the built-in profiler to get detailed information about work done in the JavaScript thread and main thread side-by-side.
Access it by selecting Perf Monitor from the Debug menu.
For iOS, Instruments is an invaluable tool, and on Android you should learn to use [`systrace`](docs/performance.html#profiling-android-ui-performance-with-systrace).
You can also use [`react-addons-perf`](https://facebook.github.io/react/docs/perf.html) to get insights into where React is spending time when rendering your components.
Another way to profile JavaScript is to use the Chrome profiler while debugging. This won't give you accurate results as the code is running in Chrome but will give you a general idea of where bottlenecks might be.
Another way to profile JavaScript is to use the Chrome profiler while debugging.
This won't give you accurate results as the code is running in Chrome but will give you a general idea of where bottlenecks might be.
But first, [**make sure that Development Mode is OFF!**](docs/performance.html#running-in-development-mode-dev-true) You should see `__DEV__ === false, development-level warning are OFF, performance optimizations are ON` in your application logs.
### Profiling Android UI Performance with `systrace`
Android supports 10k+ different phones and is generalized to support software rendering:
the framework architecture and need to generalize across many hardware targets unfortunately means you get less for free relative to iOS.
But sometimes, there are things you can improve -- and many times it's not native code's fault at all!
The first step for debugging this jank is to answer the fundamental question of where your time is being spent during each 16ms frame.
For that, we'll be using a standard Android profiling tool called `systrace`.
`systrace` is a standard Android marker-based profiling tool (and is installed when you install the Android platform-tools package).
Profiled code blocks are surrounded by markers start/end markers which are then visualized in a colorful chart format.
Both the Android SDK and React Native framework provide standard markers that you can visualize.
#### 1. Collecting a trace
First, connect a device that exhibits the stuttering you want to investigate to your computer via USB and get it to the point right before the navigation/animation you want to profile.
Run `systrace` as follows:
```
$ <path_to_android_sdk>/platform-tools/systrace/systrace.py --time=10 -o trace.html sched gfx view -a <your_package_name>
```
A quick breakdown of this command:
- `time` is the length of time the trace will be collected in seconds
- `sched`, `gfx`, and `view` are the android SDK tags (collections of markers) we care about: `sched` gives you information about what's running on each core of your phone, `gfx` gives you graphics info such as frame boundaries, and `view` gives you information about measure, layout, and draw passes
- `-a <your_package_name>` enables app-specific markers, specifically the ones built into the React Native framework. `your_package_name` can be found in the `AndroidManifest.xml` of your app and looks like `com.example.app`
Once the trace starts collecting, perform the animation or interaction you care about. At the end of the trace, systrace will give you a link to the trace which you can open in your browser.
#### 2. Reading the trace
After opening the trace in your browser (preferably Chrome), you should see something like this:
![Example](img/SystraceExample.png)
> **HINT**:
> Use the WASD keys to strafe and zoom
If your trace .html file isn't opening correctly, check your browser console for the following:
![ObjectObserveError](img/ObjectObserveError.png)
Since `Object.observe` was deprecated in recent browsers, you may have to open the file from the Google Chrome Tracing tool. You can do so by:
- Opening tab in chrome chrome://tracing
- Selecting load
- Selecting the html file generated from the previous command.
> **Enable VSync highlighting**
>
> Check this checkbox at the top right of the screen to highlight the 16ms frame boundaries:
>
> ![Enable VSync Highlighting](img/SystraceHighlightVSync.png)
>
> You should see zebra stripes as in the screenshot above.
> If you don't, try profiling on a different device: Samsung has been known to have issues displaying vsyncs while the Nexus series is generally pretty reliable.
#### 3. Find your process
Scroll until you see (part of) the name of your package.
In this case, I was profiling `com.facebook.adsmanager`,
which shows up as `book.adsmanager` because of silly thread name limits in the kernel.
On the left side, you'll see a set of threads which correspond to the timeline rows on the right.
There are a few threads we care about for our purposes:
the UI thread (which has your package name or the name UI Thread), `mqt_js`, and `mqt_native_modules`.
If you're running on Android 5+, we also care about the Render Thread.
- **UI Thread.**
This is where standard android measure/layout/draw happens.
The thread name on the right will be your package name (in my case book.adsmanager) or UI Thread.
The events that you see on this thread should look something like this and have to do with `Choreographer`, `traversals`, and `DispatchUI`:
![UI Thread Example](img/SystraceUIThreadExample.png)
- **JS Thread.**
This is where JavaScript is executed.
The thread name will be either `mqt_js` or `<...>` depending on how cooperative the kernel on your device is being.
To identify it if it doesn't have a name, look for things like `JSCall`, `Bridge.executeJSCall`, etc:
![JS Thread Example](img/SystraceJSThreadExample.png)
- **Native Modules Thread.**
This is where native module calls (e.g. the `UIManager`) are executed.
The thread name will be either `mqt_native_modules` or `<...>`.
To identify it in the latter case, look for things like `NativeCall`, `callJavaModuleMethod`, and `onBatchComplete`:
![Native Modules Thread Example](img/SystraceNativeModulesThreadExample.png)
- **Bonus: Render Thread.**
If you're using Android L (5.0) and up, you will also have a render thread in your application.
This thread generates the actual OpenGL commands used to draw your UI.
The thread name will be either `RenderThread` or `<...>`.
To identify it in the latter case, look for things like `DrawFrame` and `queueBuffer`:
![Render Thread Example](img/SystraceRenderThreadExample.png)
#### Identifying a culprit
A smooth animation should look something like the following:
![Smooth Animation](img/SystraceWellBehaved.png)
Each change in color is a frame -- remember that in order to display a frame,
all our UI work needs to be done by the end of that 16ms period.
Notice that no thread is working close to the frame boundary.
An application rendering like this is rendering at 60 FPS.
If you noticed chop, however, you might see something like this:
![Choppy Animation from JS](img/SystraceBadJS.png)
Notice that the JS thread is executing basically all the time, and across frame boundaries!
This app is not rendering at 60 FPS.
In this case, **the problem lies in JS**.
You might also see something like this:
![Choppy Animation from UI](img/SystraceBadUI.png)
In this case, the UI and render threads are the ones that have work crossing frame boundaries.
The UI that we're trying to render on each frame is requiring too much work to be done.
In this case, **the problem lies in the native views being rendered**.
At this point, you'll have some very helpful information to inform your next steps.
#### Resolving JavaScript issues
If you identified a JS problem,
look for clues in the specific JS that you're executing.
In the scenario above, we see `RCTEventEmitter` being called multiple times per frame.
Here's a zoom-in of the JS thread from the trace above:
![Too much JS](img/SystraceBadJS2.png)
This doesn't seem right.
Why is it being called so often?
Are they actually different events?
The answers to these questions will probably depend on your product code.
And many times, you'll want to look into [shouldComponentUpdate](https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate).
#### Resolving native UI Issues
If you identified a native UI problem, there are usually two scenarios:
1. the UI you're trying to draw each frame involves to much work on the GPU, or
2. You're constructing new UI during the animation/interaction (e.g. loading in new content during a scroll).
##### Too much GPU work
In the first scenario, you'll see a trace that has the UI thread and/or Render Thread looking like this:
![Overloaded GPU](img/SystraceBadUI.png)
Notice the long amount of time spent in `DrawFrame` that crosses frame boundaries. This is time spent waiting for the GPU to drain its command buffer from the previous frame.
To mitigate this, you should:
- investigate using `renderToHardwareTextureAndroid` for complex, static content that is being animated/transformed (e.g. the `Navigator` slide/alpha animations)
- make sure that you are **not** using `needsOffscreenAlphaCompositing`, which is disabled by default, as it greatly increases the per-frame load on the GPU in most cases.
If these don't help and you want to dig deeper into what the GPU is actually doing, you can check out [Tracer for OpenGL ES](http://developer.android.com/tools/help/gltracer.html).
##### Creating new views on the UI thread
In the second scenario, you'll see something more like this:
![Creating Views](img/SystraceBadCreateUI.png)
Notice that first the JS thread thinks for a bit, then you see some work done on the native modules thread, followed by an expensive traversal on the UI thread.
There isn't an easy way to mitigate this unless you're able to postpone creating new UI until after the interaction, or you are able to simplify the UI you're creating. The react native team is working on a infrastructure level solution for this that will allow new UI to be created and configured off the main thread, allowing the interaction to continue smoothly.
+2 -2
View File
@@ -4,8 +4,8 @@ title: Platform Specific Code
layout: docs
category: Guides
permalink: docs/platform-specific-code.html
next: gesture-responder-system
previous: upgrading
next: debugging
previous: colors
---
When building a cross-platform app, you'll want to re-use as much code as possible. Scenarios may arise where it makes sense for the code to be different, for example you may want to implement separate visual components for iOS and Android.
+4 -3
View File
@@ -4,8 +4,9 @@ title: Running On Device
layout: docs
category: Guides
permalink: docs/running-on-device.html
next: javascript-environment
previous: testing
banner: ejected
next: upgrading
previous: integration-with-existing-apps
---
It's always a good idea to test your app on an actual device before releasing it to your users. This document will guide you through the necessary steps to run your React Native app on a device.
@@ -55,7 +56,7 @@ Installing an app on an iOS device requires a Mac, an Apple ID, and a USB cable.
<block class="mac ios" />
Connect your device to your Mac via USB, then open Xcode. In the project navigator, choose your device from the Product > Destination toolbar menu. Xcode will then register your device for development.
Connect your device to your Mac via USB, then open Xcode. Navigate into `ios` folder of your project and open the file ending .xcodeproj inside it. In the project navigator, choose your device from the Product > Destination toolbar menu. Xcode will then register your device for development.
> If you run into any issues, please take a look at Apple's [Launching Your App on a Device docs](https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/LaunchingYourApponDevices/LaunchingYourApponDevices.html#//apple_ref/doc/uid/TP40012582-CH27-SW4).
+1
View File
@@ -4,6 +4,7 @@ title: Running On Simulator
layout: docs
category: Guides (iOS)
permalink: docs/running-on-simulator-ios.html
banner: ejected
next: communication-ios
previous: linking-libraries-ios
---
+2 -1
View File
@@ -4,7 +4,8 @@ title: Generating Signed APK
layout: docs
category: Guides (Android)
permalink: docs/signed-apk-android.html
next: android-ui-performance
banner: ejected
next: android-building-from-source
previous: headless-js-android
---
+3 -2
View File
@@ -53,8 +53,9 @@ class BlinkApp extends Component {
AppRegistry.registerComponent('BlinkApp', () => BlinkApp);
```
In a real application, you probably won't be setting state with a timer. You might set state when you have new data arrive from the server, or from user input. You can also use a state container like [Redux](http://redux.js.org/index.html) to control your data flow. In that case you would use Redux to modify your state rather than calling `setState` directly.
In a real application, you probably won't be setting state with a timer. You might set state when you have new data arrive from the server, or from user input. You can also use a state container like [Redux](http://redux.js.org/index.html) to control your data flow. In that case you would use Redux to modify your state rather than calling `setState` directly.
State works the same way as it does in React, so for more details on handling state, you can look at the [React.Component API](https://facebook.github.io/react/docs/component-api.html).
When setState is called, BlinkApp will re-render its Component. By calling setState within the Timer, the component will re-render every time the Timer ticks.
State works the same way as it does in React, so for more details on handling state, you can look at the [React.Component API](https://facebook.github.io/react/docs/component-api.html).
At this point, you might be annoyed that most of our examples so far use boring default black text. To make things more beautiful, you will have to [learn about Style](docs/style.html).
+1 -1
View File
@@ -8,7 +8,7 @@ next: height-and-width
previous: state
---
With React Native, you don't use a special language or syntax for defining styles. You just style your application using JavaScript. All of the core components accept a prop named `style`. The style names and values usually match how CSS works on the web, except names are written using camel casing, e.g `backgroundColor` rather than `background-color`.
With React Native, you don't use a special language or syntax for defining styles. You just style your application using JavaScript. All of the core components accept a prop named `style`. The style names and [values](docs/colors.html) usually match how CSS works on the web, except names are written using camel casing, e.g `backgroundColor` rather than `background-color`.
The `style` prop can be a plain old JavaScript object. That's the simplest and what we usually use for example code. You can also pass an array of styles - the last style in the array has precedence, so you can use this to inherit styles.
+64 -14
View File
@@ -4,19 +4,21 @@ title: Testing
layout: docs
category: Guides
permalink: docs/testing.html
next: running-on-device
previous: debugging
next: understanding-cli
previous: gesture-responder-system
---
## Running Tests and Contributing
This document is about running tests on React Native itself. If you're interested in testing a React Native app, check out the [React Native Tutorial](http://facebook.github.io/jest/docs/tutorial-react-native.html) on the Jest website.
The React Native repo has several tests you can run to verify you haven't caused a regression with your PR. These tests are run with the [Travis](http://docs.travis-ci.com/) and [CircleCI](https://circleci.com/) continuous integration systems, which will automatically annotate pull requests with the test results.
## Running Tests and Contributing
The React Native repo has several tests you can run to verify you haven't caused a regression with your PR. These tests are run with the [Travis](https://travis-ci.org/facebook/react-native/builds) and [CircleCI](https://circleci.com/gh/facebook/react-native) continuous integration systems, which will automatically annotate pull requests with the test results.
Whenever you are fixing a bug or adding new functionality to React Native, you should add a test that covers it. Depending on the change you're making, there are different types of tests that may be appropriate.
## Jest Tests
## JavaScript Tests
### Jest
Jest tests are JavaScript-only tests run on the command line with node. You can run the existing React Native jest tests with:
@@ -27,9 +29,25 @@ It's a good idea to add a Jest test when you are working on a change that only m
The tests themselves live in the `__tests__` directories of the files they test. See [`TouchableHighlight-test.js`](https://github.com/facebook/react-native/blob/master/Libraries/Components/Touchable/__tests__/TouchableHighlight-test.js) for a basic example.
## Android Unit Tests
### Flow
The Android unit tests do not run in an emulator. They just use a normal Java installation. You do need to install Java 8. In particular, the default OS X Java install is insufficient.
You should also make sure your code passes [Flow](https://flowtype.org/) tests. These can be run using:
$ cd react-native
$ npm run flow
## Android
### Unit Tests
The Android unit tests do not run in an emulator. They just use a normal Java installation. The default macOS Java install is insufficient, you may need to install [Java 8 (JDK8)](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html). You can type `javac -version` in a terminal to see what version you have:
```
$ javac -version
javac 1.8.0_111
```
The version string `1.8.x_xxx` corresponds to JDK 8.
You also need to install the [Buck build tool](https://buckbuild.com/setup/install.html).
@@ -40,7 +58,7 @@ To run the Android unit tests:
It's a good idea to add an Android unit test whenever you are working on code that can be tested by Java code alone. The Android unit tests live under [`ReactAndroid/src/tests`](https://github.com/facebook/react-native/tree/master/ReactAndroid/src/test/java/com/facebook/react), so you can browse through that directory for good examples of tests.
## Android Integration Tests
### Integration Tests
To run the integration tests, you need to install the Android NDK. See [Prerequisites](docs/android-building-from-source.html#prerequisites).
@@ -60,16 +78,48 @@ Once you have an emulator running, to run the integration tests:
The integration tests should only take a few minutes to run on a modern developer machine.
It's a good idea to add an Android integration test whenever you are working on code that needs both JavaScript and Java to be tested in conjunction. The Android integration tests live under [`ReactAndroid/src/androidTest`](https://github.com/facebook/react-native/tree/master/ReactAndroid/src/androidTest/java/com/facebook/react/tests), so you can browse through that directory for good examples of tests.
It's a good idea to add an Android integration test whenever you are working on code that needs both JavaScript and Java to be tested in conjunction. The Android integration tests live under [`ReactAndroid/src/androidTest`](https://github.com/facebook/react-native/tree/master/ReactAndroid/src/androidTest/java/com/facebook/react/tests), so you can browse through that directory for good examples of tests.
## iOS Integration Tests
## iOS
React Native provides facilities to make it easier to test integrated components that require both native and JS components to communicate across the bridge. The two main components are `RCTTestRunner` and `RCTTestModule`. `RCTTestRunner` sets up the ReactNative environment and provides facilities to run the tests as `XCTestCase`s in Xcode (`runTest:module` is the simplest method). `RCTTestModule` is exported to JS as `NativeModules.TestModule`. The tests themselves are written in JS, and must call `TestModule.markTestCompleted()` when they are done, otherwise the test will timeout and fail. Test failures are primarily indicated by throwing a JS exception. It is also possible to test error conditions with `runTest:module:initialProps:expectErrorRegex:` or `runTest:module:initialProps:expectErrorBlock:` which will expect an error to be thrown and verify the error matches the provided criteria. See [`IntegrationTestHarnessTest.js`](https://github.com/facebook/react-native/blob/master/IntegrationTests/IntegrationTestHarnessTest.js), [`UIExplorerIntegrationTests.m`](https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerIntegrationTests.m), and [IntegrationTestsApp.js](https://github.com/facebook/react-native/blob/master/IntegrationTests/IntegrationTestsApp.js) for example usage and integration points.
### Integration Tests
You can run integration tests locally with cmd+U in the IntegrationTest and UIExplorer apps in Xcode.
React Native provides facilities to make it easier to test integrated components that require both native and JS components to communicate across the bridge. The two main components are `RCTTestRunner` and `RCTTestModule`. `RCTTestRunner` sets up the ReactNative environment and provides facilities to run the tests as `XCTestCase`s in Xcode (`runTest:module` is the simplest method). `RCTTestModule` is exported to JS as `NativeModules.TestModule`.
## iOS Screenshot/Snapshot Tests
The tests themselves are written in JS, and must call `TestModule.markTestCompleted()` when they are done, otherwise the test will timeout and fail. Test failures are primarily indicated by throwing a JS exception. It is also possible to test error conditions with `runTest:module:initialProps:expectErrorRegex:` or `runTest:module:initialProps:expectErrorBlock:` which will expect an error to be thrown and verify the error matches the provided criteria.
See the following for example usage and integration points:
- [`IntegrationTestHarnessTest.js`](https://github.com/facebook/react-native/blob/master/IntegrationTests/IntegrationTestHarnessTest.js)
- [`UIExplorerIntegrationTests.m`](https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerIntegrationTests.m)
- [`IntegrationTestsApp.js`](https://github.com/facebook/react-native/blob/master/IntegrationTests/IntegrationTestsApp.js)
You can run integration tests locally with cmd+U in the IntegrationTest and UIExplorer apps in Xcode, or by running the following in the command line on macOS:
$ cd react-native
$ ./scripts/objc-test-ios.sh
> Your Xcode install will come with a variety of Simulators running the latest OS. You may need to manually create a new Simulator to match what the `XCODE_DESTINATION` param in the test script.
### Screenshot/Snapshot Tests
A common type of integration test is the snapshot test. These tests render a component, and verify snapshots of the screen against reference images using `TestModule.verifySnapshot()`, using the [`FBSnapshotTestCase`](https://github.com/facebook/ios-snapshot-test-case) library behind the scenes. Reference images are recorded by setting `recordMode = YES` on the `RCTTestRunner`, then running the tests. Snapshots will differ slightly between 32 and 64 bit, and various OS versions, so it's recommended that you enforce tests are run with the correct configuration. It's also highly recommended that all network data be mocked out, along with other potentially troublesome dependencies. See [`SimpleSnapshotTest`](https://github.com/facebook/react-native/blob/master/IntegrationTests/SimpleSnapshotTest.js) for a basic example.
If you make a change that affects a snapshot test in a PR, such as adding a new example case to one of the examples that is snapshotted, you'll need to re-record the snapshot reference image. To do this, simply change to `_runner.recordMode = YES;` in [UIExplorer/UIExplorerSnapshotTests.m](https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/UIExplorerIntegrationTests/UIExplorerSnapshotTests.m#L42), re-run the failing tests, then flip record back to `NO` and submit/update your PR and wait to see if the Travis build passes.
## End-to-end tests
Finally, make sure end-to-end tests run successfully by executing the following script:
$ cd react-native
$ ./scripts/test-manual-e2e.sh
## Website
The React Native website is hosted on GitHub pages and is automatically generated from Markdown sources as well as comments in the JavaScript source files. It's always a good idea to check that the website is generated properly whenever you edit the docs.
$ cd website
$ npm install
$ npm start
Then open http://localhost:8079/react-native/index.html in your browser.
+2 -2
View File
@@ -83,7 +83,7 @@ The `<Text>` element is special relative to layout: everything inside is no long
## Limited Style Inheritance
On the web, the usual way to set a font family and size for the entire document is to write:
On the web, the usual way to set a font family and size for the entire document is to take advantage of inherited CSS properties like so:
```css
/* CSS, *not* React Native */
@@ -94,7 +94,7 @@ html {
}
```
When the browser is trying to render a text node, it's going to go all the way up to the root element of the tree and find an element with a `font-size` attribute. An unexpected property of this system is that **any** node can have `font-size` attribute, including a `<div>`. This was designed for convenience, even though not really semantically correct.
All elements in the document will inherit this font unless they or one of their parents specifies a new rule.
In React Native, we are more strict about it: **you must wrap all the text nodes inside of a `<Text>` component**; you cannot have a text node directly under a `<View>`.
+1 -1
View File
@@ -4,7 +4,7 @@ title: Timers
layout: docs
category: Guides
permalink: docs/timers.html
next: direct-manipulation
next: javascript-environment
previous: accessibility
---
+9
View File
@@ -93,3 +93,12 @@ If you run into issues where running `react-native init` hangs in your system, t
```
react-native init --verbose
```
## Unable to start react-native package manager (on Linux)
### Case 1: Error "code":"ENOSPC","errno":"ENOSPC"
Issue caused by the number of directories [inotify](https://github.com/guard/listen/wiki/Increasing-the-amount-of-inotify-watchers) (used by watchman on Linux) can monitor. To solve it, just run this command in your terminal window
```
echo fs.inotify.max_user_watches=582222 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
```
+3 -3
View File
@@ -4,8 +4,9 @@ title: Understanding the CLI
layout: docs
category: Guides
permalink: docs/understanding-cli.html
next: upgrading
previous: performance
banner: ejected
next: integration-with-existing-apps
previous: running-on-device
---
Though you may have installed the `react-native-cli` via npm as a separate module, it is a shell for accessing the CLI embedded
@@ -35,4 +36,3 @@ module.exports = {
### Parameters
The command name identifies the parameters that a command would expect. When the command parameter is surrounded by greater-than, less-than symbols `< >`, this indicates that the parameter is expected. When a parameter is surrounded by brackets `[ ]`, this indicates that the parameter is optional.
+2 -1
View File
@@ -4,7 +4,8 @@ title: Upgrading
layout: docs
category: Guides
permalink: docs/upgrading.html
next: platform-specific-code
banner: ejected
next: native-modules-ios
previous: understanding-cli
---
+1 -1
View File
@@ -8,7 +8,7 @@ next: network
previous: using-a-scrollview
---
The `ListView` component displays a vertically scrolling list of changing, but similarly structured, data.
The `ListView` component displays a scrolling list of changing, but similarly structured, data.
`ListView` works well for long lists of data, where the number of items might change over time. Unlike the more generic [`ScrollView`](docs/using-a-scrollview.html), the `ListView` only renders elements that are currently showing on the screen, not all the elements at once.
+1 -1
View File
@@ -18,7 +18,7 @@ import { AppRegistry, ScrollView, Image, Text } from 'react-native'
class IScrolledDownAndWhatHappenedNextShockedMe extends Component {
render() {
return(
return (
<ScrollView>
<Text style={{fontSize:96}}>Scroll me plz</Text>
<Image source={require('./img/favicon.png')} />
-182
View File
@@ -1,182 +0,0 @@
---
id: using-navigators
title: Using Navigators
layout: docs
category: The Basics
permalink: docs/using-navigators.html
next: more-resources
previous: networking
---
Mobile apps rarely consist of just one screen. As soon as you add a second screen to your app, you will have to take into consideration how the user will navigate from one screen to the other.
You can use navigators to transition between multiple screens. These transitions can be typical side-to-side animations down a master/detail stack, or vertical modal popups.
## Navigator
React Native has several built-in navigation components, but for your first app you will probably want to use `Navigator`. It provides a JavaScript implementation of a navigation stack, so it works on both iOS and Android and is easy to customize.
![](img/NavigationStack-Navigator.gif)
### Working with Scenes
At this point you should feel comfortable rendering all sorts of components in your app, be it a simple `View` with `Text` inside, or a `ScrollView` with a list of `Image`s. Together, these components make up a scene (another word for screen) in your app.
A scene is nothing other than a React component that is typically rendered full screen. This is in contrast to a `Text`, an `Image`, or even a custom `SpinningBeachball` component that is meant to be rendered as part of a screen. You may have already used one without realizing it - the ["HelloWorldApp"](docs/tutorial.html), the ["FlexDirectionBasics"](docs/flexbox.html), and the ["ListViewBasics"](docs/using-a-listview.html) components covered earlier in the tutorial are all examples of scenes.
For simplicity's sake, let's define a simple scene that displays a bit of text. We will come back to this scene later as we add navigation to our app. Create a new file called "MyScene.js" with the following contents:
```javascript
import React, { Component } from 'react';
import { View, Text, Navigator } from 'react-native';
export default class MyScene extends Component {
static get defaultProps() {
return {
title: 'MyScene'
};
}
render() {
return (
<View>
<Text>Hi! My name is {this.props.title}.</Text>
</View>
)
}
}
```
Notice the `export default` in front of the component declaration. This will _export_ the component, and in turn allow other components to _import_ it later on, like so:
```javascript
import React, { Component } from 'react';
import { AppRegistry } from 'react-native';
import MyScene from './MyScene';
class YoDawgApp extends Component {
render() {
return (
<MyScene />
)
}
}
AppRegistry.registerComponent('YoDawgApp', () => YoDawgApp);
```
We now have a simple app that renders your scene and nothing else. In this case, `MyScene` is a simple example of a [reusable React component](https://facebook.github.io/react/docs/reusable-components.html).
### Using Navigator
Enough about scenes, let's start navigating. We will start by rendering a `Navigator`, and then let the `Navigator` render the scene for you by passing in your own render function to its `renderScene` prop.
```javascript
render() {
return (
<Navigator
initialRoute={{ title: 'My Initial Scene', index: 0 }}
renderScene={(route, navigator) => {
return <MyScene title={route.title} />
}}
/>
);
}
```
Something you will encounter a lot when dealing with navigation is the concept of routes. A route is an object that contains information about a scene. It is used to provide all the context that the navigator's `renderScene` function needs to render a scene. It can have any number of keys to help distinguish your scene, and I happened to pick a single `title` key for the above example.
#### Pushing scenes onto the stack
In order to transition to a new scene, you will need to learn about `push` and `pop`. These two methods are provided by the `navigator` object that is passed to your `renderScene` function above. They can be used, as you may have realized, to push and pop routes into your navigation stack.
```javascript
navigator.push({
title: 'Next Scene',
index: 1,
});
navigator.pop();
```
A more complete example that demonstrates the pushing and popping of routes. Edit your index*.js file to look something like this:
```javascript
import React, { Component } from 'react';
import { AppRegistry, Navigator } from 'react-native';
import MyScene from './MyScene';
class SimpleNavigationApp extends Component {
render() {
return (
<Navigator
initialRoute={{ title: 'My Initial Scene', index: 0 }}
renderScene={(route, navigator) =>
<MyScene
title={route.title}
// Function to call when a new scene should be displayed
onForward={() => {
const nextIndex = route.index + 1;
navigator.push({
title: 'Scene ' + nextIndex,
index: nextIndex,
});
}}
// Function to call to go back to the previous scene
onBack={() => {
if (route.index > 0) {
navigator.pop();
}
}}
/>
}
/>
)
}
}
AppRegistry.registerComponent('SimpleNavigationApp', () => SimpleNavigationApp);
```
And your MyScene.js to match this:
```javascript
import React, { Component, PropTypes } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
export default class MyScene extends Component {
render() {
return (
<View>
<Text>Current Scene: {this.props.title}</Text>
<TouchableHighlight onPress={this.props.onForward}>
<Text>Tap me to load the next scene</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.props.onBack}>
<Text>Tap me to go back</Text>
</TouchableHighlight>
</View>
)
}
}
MyScene.propTypes = {
title: PropTypes.string.isRequired,
onForward: PropTypes.func.isRequired,
onBack: PropTypes.func.isRequired,
};
```
In this example, the `MyScene` component is passed the title of the current route via the `title` prop. It displays two tappable components that call the `onForward` and `onBack` functions passed through its props, which in turn will call `navigator.push()` and `navigator.pop()` as needed.
Check out the [Navigator API reference](docs/navigator.html) for more `Navigator` code samples, or read through the [Navigation guide](docs/navigation.html) for other examples of what you can do with navigators.
## High Five!
If you've gotten here by reading linearly through the tutorial, then you are a pretty impressive human being. Congratulations. Next, you might want to check out [all the cool stuff the community does with React Native](docs/more-resources.html).
+1
View File
@@ -42,6 +42,7 @@ const documentedCommands = [
require('./library/library'),
require('./bundle/bundle'),
require('./bundle/unbundle'),
require('./eject/eject'),
require('./link/link'),
require('./link/unlink'),
require('./install/install'),
+96
View File
@@ -0,0 +1,96 @@
/**
* 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.
*/
'use strict';
const copyProjectTemplateAndReplace = require('../generator/copyProjectTemplateAndReplace');
const path = require('path');
const fs = require('fs');
/**
* The eject command re-creates the `android` and `ios` native folders. Because native code can be
* difficult to maintain, this new script allows an `app.json` to be defined for the project, which
* is used to configure the native app.
*
* The `app.json` config may contain the following keys:
*
* - `name` - The short name used for the project, should be TitleCase
* - `displayName` - The app's name on the home screen
*/
function eject() {
const doesIOSExist = fs.existsSync(path.resolve('ios'));
const doesAndroidExist = fs.existsSync(path.resolve('android'));
if (doesIOSExist && doesAndroidExist) {
console.error(
'Both the iOS and Android folders already exist! Please delete `ios` and/or `android` ' +
'before ejecting.'
);
process.exit(1);
}
let appConfig = null;
try {
appConfig = require(path.resolve('app.json'));
} catch(e) {
console.error(
`Eject requires an \`app.json\` config file to be located at ` +
`${path.resolve('app.json')}, and it must at least specify a \`name\` for the project ` +
`name, and a \`displayName\` for the app's home screen label.`
);
process.exit(1);
}
const appName = appConfig.name;
if (!appName) {
console.error(
`App \`name\` must be defined in the \`app.json\` config file to define the project name. `+
`It must not contain any spaces or dashes.`
);
process.exit(1);
}
const displayName = appConfig.displayName;
if (!displayName) {
console.error(
`App \`displayName\` must be defined in the \`app.json\` config file, to define the label ` +
`of the app on the home screen.`
);
process.exit(1);
}
const templateOptions = { displayName };
if (!doesIOSExist) {
console.log('Generating the iOS folder.');
copyProjectTemplateAndReplace(
path.resolve('node_modules', 'react-native', 'local-cli', 'templates', 'HelloWorld', 'ios'),
path.resolve('ios'),
appName,
templateOptions
);
}
if (!doesAndroidExist) {
console.log('Generating the Android folder.');
copyProjectTemplateAndReplace(
path.resolve('node_modules', 'react-native', 'local-cli', 'templates', 'HelloWorld', 'android'),
path.resolve('android'),
appName,
templateOptions
);
}
}
module.exports = {
name: 'eject',
description: 'Re-create the iOS and Android folders and native code',
func: eject,
options: [],
};
@@ -27,10 +27,12 @@ function copyProjectTemplateAndReplace(srcPath, destPath, newProjectName, option
if (!destPath) { throw new Error('Need a path to copy to'); }
if (!newProjectName) { throw new Error('Need a project name'); }
options = options || {};
walk(srcPath).forEach(absoluteSrcFilePath => {
// 'react-native upgrade'
if (options && options.upgrade) {
if (options.upgrade) {
// Don't upgrade these files
const fileName = path.basename(absoluteSrcFilePath);
// This also includes __tests__/index.*.js
@@ -44,7 +46,7 @@ function copyProjectTemplateAndReplace(srcPath, destPath, newProjectName, option
.replace(/helloworld/g, newProjectName.toLowerCase());
let contentChangedCallback = null;
if (options && options.upgrade && (!options.force)) {
if (options.upgrade && (!options.force)) {
contentChangedCallback = (_, contentChanged) => {
return upgradeFileContentChangedCallback(
absoluteSrcFilePath,
@@ -57,6 +59,7 @@ function copyProjectTemplateAndReplace(srcPath, destPath, newProjectName, option
absoluteSrcFilePath,
path.resolve(destPath, relativeRenamedPath),
{
'Hello App Display Name': options.displayName || newProjectName,
'HelloWorld': newProjectName,
'helloworld': newProjectName.toLowerCase(),
},
+112
View File
@@ -0,0 +1,112 @@
/**
* 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.
*/
'use strict';
const copyProjectTemplateAndReplace = require('./copyProjectTemplateAndReplace');
const execSync = require('child_process').execSync;
const fs = require('fs');
const path = require('path');
const availableTemplates = {
navigation: 'HelloNavigation',
};
function listTemplatesAndExit(newProjectName, options) {
if (options.template === true) {
// Just listing templates using 'react-native init --template'.
// Not creating a new app.
// Print available templates and exit.
const templateKeys = Object.keys(availableTemplates);
if (templateKeys.length === 0) {
// Just a guard, should never happen as long availableTemplates
// above is defined correctly :)
console.log(
'There are no templates available besides ' +
'the default "Hello World" one.'
);
} else {
console.log(
'The available templates are:\n' +
templateKeys.join('\n') +
'\nYou can use these to create an app based on a template, for example: ' +
'you could run: ' +
'react-native init ' + newProjectName + ' --template ' + templateKeys[0]
);
}
// Exit 'react-native init'
return true;
}
// Continue 'react-native init'
return false;
}
/**
* @param newProjectName For example 'AwesomeApp'.
* @param templateKey Template to use, for example 'navigation'.
* @param yarnVersion Version of yarn available on the system, or null if
* yarn is not available. For example '0.18.1'.
*/
function createProjectFromTemplate(destPath, newProjectName, templateKey, yarnVersion) {
// Expand the basic 'HelloWorld' template
copyProjectTemplateAndReplace(
path.resolve('node_modules', 'react-native', 'local-cli', 'templates', 'HelloWorld'),
destPath,
newProjectName
);
if (templateKey !== undefined) {
// Keep the files from the 'HelloWorld' template, and overwrite some of them
// with the specified project template.
// The 'HelloWorld' template contains the native files (these are used by
// all templates) and every other template only contains additional JS code.
// Reason:
// This way we don't have to duplicate the native files in every template.
// If we duplicated them we'd make RN larger and risk that people would
// forget to maintain all the copies so they would go out of sync.
const templateName = availableTemplates[templateKey];
if (templateName) {
copyProjectTemplateAndReplace(
path.resolve(
'node_modules', 'react-native', 'local-cli', 'templates', templateName
),
destPath,
newProjectName
);
} else {
throw new Error('Uknown template: ' + templateKey);
}
// Add dependencies:
// dependencies.json is a special file that lists additional dependencies
// that are required by this template
const dependenciesJsonPath = path.resolve(
'node_modules', 'react-native', 'local-cli', 'templates', templateName, 'dependencies.json'
);
if (fs.existsSync(dependenciesJsonPath)) {
console.log('Adding dependencies for the project...');
const dependencies = JSON.parse(fs.readFileSync(dependenciesJsonPath));
for (let depName in dependencies) {
const depVersion = dependencies[depName];
const depToInstall = depName + '@' + depVersion;
console.log('Adding ' + depToInstall + '...');
if (yarnVersion) {
execSync(`yarn add ${depToInstall}`, {stdio: 'inherit'});
} else {
execSync(`npm install ${depToInstall} --save --save-exact`, {stdio: 'inherit'});
}
}
}
}
}
module.exports = {
listTemplatesAndExit,
createProjectFromTemplate,
};
+16 -10
View File
@@ -8,7 +8,10 @@
*/
'use strict';
const copyProjectTemplateAndReplace = require('../generator/copyProjectTemplateAndReplace');
const {
listTemplatesAndExit,
createProjectFromTemplate,
} = require('../generator/templates');
const execSync = require('child_process').execSync;
const fs = require('fs');
const minimist = require('minimist');
@@ -23,15 +26,15 @@ const yarn = require('../util/yarn');
* @param projectDir Templates will be copied here.
* @param argsOrName Project name or full list of custom arguments
* for the generator.
* @param options Command line options passed from the react-native-cli directly.
* E.g. `{ version: '0.43.0', template: 'navigation' }`
*/
function init(projectDir, argsOrName) {
console.log('Setting up new React Native app in ' + projectDir);
const args = Array.isArray(argsOrName)
? argsOrName // argsOrName was e.g. ['AwesomeApp', '--verbose']
: [argsOrName].concat(process.argv.slice(4)); // argsOrName was e.g. 'AwesomeApp'
// args array is e.g. ['AwesomeApp', '--verbose']
// args array is e.g. ['AwesomeApp', '--verbose', '--template', 'navigation']
if (!args || args.length === 0) {
console.error('react-native init requires a project name.');
return;
@@ -40,7 +43,14 @@ function init(projectDir, argsOrName) {
const newProjectName = args[0];
const options = minimist(args);
generateProject(projectDir, newProjectName, options);
if (listTemplatesAndExit(newProjectName, options)) {
// Just listing templates using 'react-native init --template'
// Not creating a new app.
return;
} else {
console.log('Setting up new React Native app in ' + projectDir);
generateProject(projectDir, newProjectName, options);
}
}
/**
@@ -67,11 +77,7 @@ function generateProject(destinationRoot, newProjectName, options) {
yarn.getYarnVersionIfAvailable() &&
yarn.isGlobalCliUsingYarn(destinationRoot);
copyProjectTemplateAndReplace(
path.resolve('node_modules', 'react-native', 'local-cli', 'templates', 'HelloWorld'),
destinationRoot,
newProjectName
);
createProjectFromTemplate(destinationRoot, newProjectName, options.template, yarnVersion);
if (yarnVersion) {
console.log('Adding React...');
+1 -1
View File
@@ -193,7 +193,7 @@ function runOnAllDevices(args, cmd, packageName, adbPath){
}
console.log(chalk.bold(
`Building and installing the app on the device (cd android && ${cmd} ${gradleArgs.join(' ')}...`
`Building and installing the app on the device (cd android && ${cmd} ${gradleArgs.join(' ')})...`
));
child_process.execFileSync(cmd, gradleArgs, {
@@ -1,2 +1,3 @@
/captures/*
preLoadedCapture.js
bundle.js
File diff suppressed because one or more lines are too long
@@ -0,0 +1,41 @@
# App template for new React Native apps
This is a simple React Native app template which demonstrates a few basics concepts such as navigation between a few screens, ListViews, and handling text input.
<img src="https://cloud.githubusercontent.com/assets/346214/22697898/ced66f52-ed4a-11e6-9b90-df6daef43199.gif" alt="Android Example" height="800" style="float: left"/>
<img src="https://cloud.githubusercontent.com/assets/346214/22697901/cfeab3e4-ed4a-11e6-8552-d76585317ac2.gif" alt="iOS Example" height="800"/>
## Purpose
The idea is to make it easier for people to get started with React Native. Currently `react-native init` creates a very simple app that contains one screen with static text. Everyone new to React Native then needs to figure out how to do very basic things such as:
- Rendering a list of items fetched from a server
- Navigating between screens
- Handling text input and the software keyboard
This app serves as a template used by `react-native init` so it is easier for anyone to get up and running quickly by having an app with a few screens and a ListView ready to go.
### Best practices
Another purpose of this app is to define best practices such as the folder structure of a standalone React Native app and naming conventions.
## Not using Redux
This template intentionally doesn't use Redux. After discussing with a few people who have experience using Redux we concluded that adding Redux to this app targeted at beginners would make the code more confusing, and wouldn't clearly show the benefits of Redux (because the app is too small). There are already a few concepts to grasp - the React component lifecycle, rendeing lists, using async / await, handling the software keyboard. We thought that's the maximum amount of things to learn at once. It's better for everyone to see patterns in their codebase as the app grows and decide for themselves whether and when they need Redux. See also the post [You Might Not Need Redux](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367#.f3q7kq4b3) by [Dan Abramov](https://twitter.com/dan_abramov).
## Not using Flow (for now)
Many people are new to React Native, some are new to ES6 and most people will be new to Flow. Therefore we didn't want to introduce all these concepts all at once in a single codebase. However, it might make sense to later introduce a separate version of this template that uses Flow annotations.
## Provide feedback
We need your feedback. Do you have a lot of experience building React Native apps? If so, please carefully read the code of the template and if you think something should be done differently, use issues in the repo [mkonicek/AppTemplateFeedback](https://github.com/mkonicek/AppTemplateFeedback) to discuss what should be done differently.
## How to use the template
```
$ react-native init MyApp --version 0.42.0-rc.2 --template navigation
$ cd MyApp
$ react-native run-android
$ react-native run-ios
```
@@ -1,3 +1,5 @@
'use strict';
/* @flow */
import React, { PropTypes, Component } from 'react';
@@ -10,7 +12,7 @@ import {
} from 'react-native';
type Props = {
offset?: number;
offset?: number,
}
type State = {
@@ -41,7 +43,7 @@ type State = {
*/
const KeyboardSpacer = () => (
Platform.OS === 'ios' ? <KeyboardSpacerIOS /> : null
)
);
class KeyboardSpacerIOS extends Component<Props, Props, State> {
static propTypes = {
@@ -1,3 +1,5 @@
'use strict';
import React, { Component } from 'react';
import {
Platform,
@@ -21,7 +23,7 @@ const Touchable = ({onPress, children}) => {
);
} else {
return (
<TouchableHighlight onPress={onPress} underlayColor='#ddd'>
<TouchableHighlight onPress={onPress} underlayColor="#ddd">
{child}
</TouchableHighlight>
);
@@ -0,0 +1,3 @@
{
"react-navigation": "1.0.0-beta.1"
}
@@ -2,4 +2,4 @@ import { AppRegistry } from 'react-native';
import MainNavigator from './views/MainNavigator';
AppRegistry.registerComponent('ChatExample', () => MainNavigator);
AppRegistry.registerComponent('HelloWorld', () => MainNavigator);
@@ -2,4 +2,4 @@ import { AppRegistry } from 'react-native';
import MainNavigator from './views/MainNavigator';
AppRegistry.registerComponent('ChatExample', () => MainNavigator);
AppRegistry.registerComponent('HelloWorld', () => MainNavigator);
@@ -0,0 +1,117 @@
'use strict';
// This file just a dummy example of a HTTP API to talk to the backend.
// The state of the "database" that would normally live on the server
// is simply held here in memory.
const backendStateForLoggedInPerson = {
chats: [
{
name: 'Claire',
messages: [
{
name: 'Claire',
text: 'I ❤️ React Native!',
},
],
},
{
name: 'John',
messages: [
{
name: 'John',
text: 'I ❤️ React Native!',
},
],
}
],
};
/**
* Randomly simulate network failures.
* It is useful to enable this during development to make sure our app works
* in real-world conditions.
*/
function isNetworkFailure() {
const chanceOfFailure = 0; // 0..1
return Math.random() < chanceOfFailure;
}
/**
* Helper for the other functions in this file.
* Simulates a short delay and then returns a provided value or failure.
* This is just a dummy example. Normally we'd make a HTTP request,
* see http://facebook.github.io/react-native/docs/network.html
*/
function _makeSimulatedNetworkRequest(getValue) {
const durationMs = 400;
return new Promise(function (resolve, reject) {
setTimeout(function () {
if (isNetworkFailure()) {
reject(new Error('Network failure'));
} else {
getValue(resolve, reject);
}
}, durationMs);
});
}
/**
* Fetch a list of all chats for the logged in person.
*/
async function fetchChatList() {
return _makeSimulatedNetworkRequest((resolve, reject) => {
resolve(backendStateForLoggedInPerson.chats.map(chat => chat.name));
});
}
/**
* Fetch a single chat.
*/
async function fetchChat(name) {
return _makeSimulatedNetworkRequest((resolve, reject) => {
resolve(
backendStateForLoggedInPerson.chats.find(
chat => chat.name === name
)
);
});
}
/**
* Send given message to given person.
*/
async function sendMessage({name, message}) {
return _makeSimulatedNetworkRequest((resolve, reject) => {
const chatForName = backendStateForLoggedInPerson.chats.find(
chat => chat.name === name
);
if (chatForName) {
chatForName.messages.push({
name: 'Me',
text: message,
});
resolve();
} else {
reject(new Error('Uknown person: ' + name));
}
});
}
const Backend = {
fetchChatList,
fetchChat,
sendMessage,
};
export default Backend;
// In case you are looking into using Redux for state management,
// this is how network requests are done in the f8 app which uses Redux:
// - To load some data, a Component fires a Redux action, such as loadSession()
// - That action makes the HTTP requests and then dispatches a redux action
// {type: 'LOADED_SESSIONS', results}
// - Then all reducers get called and one of them updates a part of the application
// state by storing the results
// - Redux re-renders the connected Components
// See https://github.com/fbsamples/f8app/search?utf8=%E2%9C%93&q=loaded_sessions
@@ -1,24 +1,20 @@
import React, { Component } from 'react';
import {
ListView,
Platform,
Text,
} from 'react-native';
'use strict';
import { TabNavigator } from 'react-navigation';
import ChatListScreen from './chat/ChatListScreen';
import FriendListScreen from './friends/FriendListScreen';
import WelcomeScreen from './welcome/WelcomeScreen';
/**
* Screen with tabs shown on app startup.
*/
const HomeScreenTabNavigator = TabNavigator({
Welcome: {
screen: WelcomeScreen,
},
Chats: {
screen: ChatListScreen,
},
Friends: {
screen: FriendListScreen,
},
});
export default HomeScreenTabNavigator;
@@ -1,3 +1,5 @@
'use strict';
/**
* This is an example React Native app demonstrates ListViews, text input and
* navigation between a few screens.
@@ -1,11 +1,16 @@
'use strict';
import React, { Component } from 'react';
import {
ActivityIndicator,
Image,
ListView,
Platform,
StyleSheet,
View,
} from 'react-native';
import ListItem from '../../components/ListItem';
import Backend from '../../lib/Backend';
export default class ChatListScreen extends Component {
@@ -29,28 +34,47 @@ export default class ChatListScreen extends Component {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([
'Claire', 'John'
])
isLoading: true,
dataSource: ds,
};
}
async componentDidMount() {
const chatList = await Backend.fetchChatList();
this.setState((prevState) => ({
dataSource: prevState.dataSource.cloneWithRows(chatList),
isLoading: false,
}));
}
// Binding the function so it can be passed to ListView below
// and 'this' works properly inside _renderRow
_renderRow = (name) => {
// and 'this' works properly inside renderRow
renderRow = (name) => {
return (
<ListItem
label={name}
onPress={() => this.props.navigation.navigate('Chat', {name: name})}
onPress={() => {
// Start fetching in parallel with animating
this.props.navigation.navigate('Chat', {
name: name,
});
}}
/>
)
);
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.loadingScreen}>
<ActivityIndicator />
</View>
);
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
renderRow={this.renderRow}
style={styles.listView}
/>
);
@@ -58,6 +82,11 @@ export default class ChatListScreen extends Component {
}
const styles = StyleSheet.create({
loadingScreen: {
backgroundColor: 'white',
paddingTop: 8,
flex: 1,
},
listView: {
backgroundColor: 'white',
},
@@ -1,14 +1,17 @@
'use strict';
import React, { Component } from 'react';
import {
ActivityIndicator,
Button,
ListView,
Platform,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import KeyboardSpacer from '../../components/KeyboardSpacer';
import Backend from '../../lib/Backend';
export default class ChatScreen extends Component {
@@ -19,23 +22,58 @@ export default class ChatScreen extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
const messages = [
{
name: props.navigation.state.params.name,
name: 'Claire',
text: 'I ❤️ React Native!',
},
];
this.state = {
messages: messages,
dataSource: ds.cloneWithRows(messages),
messages: [],
dataSource: ds,
myMessage: '',
isLoading: true,
};
}
async componentDidMount() {
let chat;
try {
chat = await Backend.fetchChat(this.props.navigation.state.params.name);
} catch (err) {
// Here we would handle the fact the request failed, e.g.
// set state to display "Messages could not be loaded".
// We should also check network connection first before making any
// network requests - maybe we're offline? See React Native's NetInfo
// module.
this.setState({
isLoading: false,
});
return;
}
this.setState((prevState) => ({
messages: chat.messages,
dataSource: prevState.dataSource.cloneWithRows(chat.messages),
isLoading: false,
}));
}
onAddMessage = async () => {
// Optimistically update the UI
this.addMessageLocal();
// Send the request
try {
await Backend.sendMessage({
name: this.props.navigation.state.params.name,
// TODO Is reading state like this outside of setState OK?
// Can it contain a stale value?
message: this.state.myMessage,
});
} catch (err) {
// Here we would handle the request failure, e.g. call setState
// to display a visual hint showing the message could not be sent.
}
}
addMessage = () => {
addMessageLocal = () => {
this.setState((prevState) => {
if (!prevState.myMessage) return prevState;
if (!prevState.myMessage) {
return prevState;
}
const messages = [
...prevState.messages, {
name: 'Me',
@@ -48,10 +86,10 @@ export default class ChatScreen extends Component {
myMessage: '',
}
});
this.refs.textInput.clear();
this.textInput.clear();
}
myMessageChange = (event) => {
onMyMessageChange = (event) => {
this.setState({myMessage: event.nativeEvent.text});
}
@@ -63,10 +101,16 @@ export default class ChatScreen extends Component {
)
render() {
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator />
</View>
);
}
return (
<View style={styles.container}>
<ListView
ref="listView"
dataSource={this.state.dataSource}
renderRow={this.renderRow}
style={styles.listView}
@@ -74,17 +118,17 @@ export default class ChatScreen extends Component {
/>
<View style={styles.composer}>
<TextInput
ref='textInput'
ref={(textInput) => { this.textInput = textInput; }}
style={styles.textInput}
placeholder='Type a message...'
placeholder="Type a message..."
text={this.state.myMessage}
onSubmitEditing={this.addMessage}
onChange={this.myMessageChange}
onSubmitEditing={this.onAddMessage}
onChange={this.onMyMessageChange}
/>
{this.state.myMessage !== '' && (
<Button
title="Send"
onPress={this.addMessage}
onPress={this.onAddMessage}
/>
)}
</View>
@@ -99,7 +143,6 @@ const styles = StyleSheet.create({
flex: 1,
padding: 8,
backgroundColor: 'white',
alignItems: 'flex-end',
},
listView: {
flex: 1,
@@ -1,18 +1,19 @@
'use strict';
import React, { Component } from 'react';
import {
Image,
Platform,
StyleSheet,
Text,
View,
} from 'react-native';
import ListItem from '../../components/ListItem';
import WelcomeText from './WelcomeText';
export default class FriendListScreen extends Component {
export default class WelcomeScreen extends Component {
static navigationOptions = {
title: 'Friends',
title: 'Welcome',
header: {
visible: Platform.OS === 'ios',
},
@@ -20,7 +21,7 @@ export default class FriendListScreen extends Component {
icon: ({ tintColor }) => (
<Image
// Using react-native-vector-icons works here too
source={require('./friend-icon.png')}
source={require('./welcome-icon.png')}
style={[styles.icon, {tintColor: tintColor}]}
/>
),
@@ -29,19 +30,12 @@ export default class FriendListScreen extends Component {
render() {
return (
<View style={styles.container}>
<Text>A list of friends here.</Text>
</View>
<WelcomeText />
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
flex: 1,
padding: 16,
},
icon: {
width: 30,
height: 26,
@@ -0,0 +1,52 @@
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
} from 'react-native';
export default class WelcomeText extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
This app shows the basics of navigating between a few screens,
working with ListView and handling text input.
</Text>
<Text style={styles.instructions}>
Modify any files to get started. For example try changing the
file views/welcome/WelcomeText.android.js.
</Text>
<Text style={styles.instructions}>
Double tap R on your keyboard to reload,{'\n'}
Shake or press menu button for dev menu.
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white',
padding: 20,
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 16,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 12,
},
});
@@ -0,0 +1,52 @@
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
} from 'react-native';
export default class WelcomeText extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
This app shows the basics of navigating between a few screens,
working with ListView and handling text input.
</Text>
<Text style={styles.instructions}>
Modify any files to get started. For example try changing the
file{'\n'}views/welcome/WelcomeText.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: 'white',
padding: 20,
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 16,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 12,
},
});

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

@@ -1,3 +1,3 @@
<resources>
<string name="app_name">HelloWorld</string>
<string name="app_name">Hello App Display Name</string>
</resources>
+4
View File
@@ -0,0 +1,4 @@
{
"name": "HelloWorld",
"displayName": "HelloWorld"
}
@@ -4,6 +4,8 @@
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Hello App Display Name</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "react-native",
"version": "1000.0.0",
"version": "0.42.3",
"description": "A framework for building native apps using React",
"license": "BSD-3-Clause",
"repository": {
@@ -115,7 +115,7 @@
"react-native": "local-cli/wrong-react-native.js"
},
"peerDependencies": {
"react": "~15.4.0-rc.4"
"react": "~15.4.1"
},
"dependencies": {
"absolute-path": "^0.0.0",
@@ -205,10 +205,10 @@
"jest-repl": "18.0.0",
"jest-runtime": "18.0.0",
"mock-fs": "^3.11.0",
"react": "~15.4.0-rc.4",
"react-dom": "~15.4.0-rc.4",
"react-test-renderer": "~15.4.0-rc.4",
"react": "~15.4.1",
"react-dom": "~15.4.1",
"react-test-renderer": "~15.4.1",
"shelljs": "0.6.0",
"sinon": "^2.0.0-pre.2"
}
}
}
+4 -3
View File
@@ -48,6 +48,7 @@ var semver = require('semver');
* if you are in a RN app folder
* init - to create a new project and npm install it
* --verbose - to print logs while init
* --template - name of the template to use, e.g. --template navigation
* --version <alternative react-native package> - override default (https://registry.npmjs.org/react-native@latest),
* package to install, examples:
* - "0.22.0-rc1" - A new app will be created using a specific version of React Native from npm repo
@@ -129,7 +130,8 @@ if (cli) {
' Options:',
'',
' -h, --help output usage information',
' -v, --version output the version number',
' -v, --version use a specific version of React Native',
' --template use an app template. Use --template to see available templates.',
'',
].join('\n'));
process.exit(0);
@@ -264,8 +266,7 @@ function getInstallPackage(rnPackage) {
}
function run(root, projectName, options) {
// E.g. '0.38' or '/path/to/archive.tgz'
const rnPackage = options.version;
const rnPackage = options.version; // e.g. '0.38' or '/path/to/archive.tgz'
const forceNpmClient = options.npm;
const yarnVersion = (!forceNpmClient) && getYarnVersionIfAvailable();
var installCommand;
Regular → Executable
+15 -6
View File
@@ -1,3 +1,4 @@
#!/usr/bin/env node
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
@@ -17,6 +18,13 @@
/*eslint-disable no-undef */
require(`shelljs/global`);
const minimist = require('minimist');
let argv = minimist(process.argv.slice(2), {
alias: {remote: 'r'},
default: {remote: 'origin'},
});
// - check we are in release branch, e.g. 0.33-stable
let branch = exec(`git symbolic-ref --short HEAD`, {silent: true}).stdout.trim();
@@ -30,7 +38,7 @@ let versionMajor = branch.slice(0, branch.indexOf(`-stable`));
// - check that argument version matches branch
// e.g. 0.33.1 or 0.33.0-rc4
let version = process.argv[2];
let version = argv._[0];
if (!version || version.indexOf(versionMajor) !== 0) {
echo(`You must pass a tag like ${versionMajor}.[X]-rc[Y] to bump a version`);
exit(1);
@@ -47,7 +55,7 @@ if (sed(`-i`, /^VERSION_NAME=.*/, `VERSION_NAME=${version}`, `ReactAndroid/gradl
}
// - change React.podspec
if (sed(`-i`, /s.version\s*=.*/, `s.version = \"${version}\"`, `React.podspec`).code) {
if (sed(`-i`, /s\.version\s*=.*/, `s.version = \"${version}\"`, `React.podspec`).code) {
echo(`Couldn't update version for React.podspec`);
exit(1);
}
@@ -77,17 +85,18 @@ if (exec(`git tag v${version}`).code) {
}
// Push newly created tag
exec(`git push origin v${version}`);
let remote = argv.remote;
exec(`git push ${remote} v${version}`);
// Tag latest if doing stable release
if (version.indexOf(`rc`) === -1) {
exec(`git tag -d latest`);
exec(`git push origin :latest`);
exec(`git push ${remote} :latest`);
exec(`git tag latest`);
exec(`git push origin latest`);
exec(`git push ${remote} latest`);
}
exec(`git push origin ${branch} --follow-tags`);
exec(`git push ${remote} ${branch} --follow-tags`);
exit(0);
/*eslint-enable no-undef */
+62
View File
@@ -0,0 +1,62 @@
#!/bin/bash
set -ex
SCRIPTS=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
ROOT=$(dirname $SCRIPTS)
YOGA_ROOT="$ROOT/ReactCommon/yoga"
# Specify `SPEC_REPO` as an env variable if you want to push to a specific spec repo.
# Defaults to `react-test`, which is meant to be a dummy repo used to test that the specs fully lint.
: ${SPEC_REPO:="react-test"}
SPEC_REPO_DIR="$HOME/.cocoapods/repos/$SPEC_REPO"
# If the `SPEC_REPO` does not exist yet, assume this is purely for testing and create a dummy repo.
if ! [ -d "$SPEC_REPO_DIR" ]; then
mkdir -p "$SPEC_REPO_DIR"
cd "$SPEC_REPO_DIR"
echo "testing" > .gitkeep
git init
git add .gitkeep
git commit -m "init"
git remote add origin "https://example.com/$SPEC_REPO.git"
fi
cd "$SPEC_REPO_DIR"
SPEC_REPO_REMOTE=$(git remote get-url origin)
POD_LINT_OPT="--verbose --no-subspecs --allow-warnings --fail-fast --private --swift-version=3.0 --sources=$SPEC_REPO_REMOTE"
# Get the version from a podspec.
version() {
ruby -rcocoapods-core -rjson -e "puts Pod::Specification.from_file('$1').version"
}
# Lint both framework and static library builds.
lint() {
pod lib lint $POD_LINT_OPT
pod lib lint $POD_LINT_OPT --use-libraries
}
# Push the spec in arg `$1`, which is expected to be in the cwd, to the `SPEC_REPO` in JSON format.
push() {
local SPEC_NAME=$1
local POD_NAME=$(basename $SPEC_NAME .podspec)
local SPEC_DIR="$SPEC_REPO_DIR/$POD_NAME/$(version $SPEC_NAME)"
local SPEC_PATH="$SPEC_DIR/$SPEC_NAME.json"
mkdir -p $SPEC_DIR
env INSTALL_YOGA_WITHOUT_PATH_OPTION=1 pod ipc spec $SPEC_NAME > $SPEC_PATH
}
# Perform linting and publishing of podspec in cwd.
# Skip linting with `SKIP_LINT` if e.g. publishing to a private spec repo.
process() {
cd $1
if [ -z "$SKIP_LINT" ]; then
lint
fi
local SPEC_NAME=(*.podspec)
push $SPEC_NAME
}
process $YOGA_ROOT
process $ROOT
+1 -1
View File
@@ -83,8 +83,8 @@ var DocsSidebar = React.createClass({
{category.links.map((metadata) =>
<li key={metadata.id}>
<a
style={{marginLeft: 10}}
target={metadata.permalink.match(/^https?:/) && '_blank'}
style={{marginLeft: metadata.indent ? 20 : 0}}
className={metadata.id === this.props.metadata.id ? 'active' : ''}
href={this.getLink(metadata)}>
{metadata.title}
+1 -1
View File
@@ -17,7 +17,7 @@ var Hero = React.createClass({
return (
<div className="hero">
<div className="wrap">
<div className="text"><strong>{this.props.title}</strong></div>
<div className="text">{this.props.title}</div>
<div className="minitext">
{this.props.subtitle}
</div>
+26
View File
@@ -0,0 +1,26 @@
/**
* 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 ShowcaseAppIcon
*/
'use strict';
var React = require('React');
const ShowcaseAppIcon = React.createClass({
render: function() {
return (
<a href={this.props.linkUri}>
<img src={this.props.iconUri} alt={this.props.name} />
</a>
);
}
});
module.exports = ShowcaseAppIcon;
+1 -1
View File
@@ -156,7 +156,7 @@ var Site = React.createClass({
<a href="http://stackoverflow.com/questions/tagged/react-native" target="_blank">Stack Overflow</a>
<a href="https://discord.gg/0ZcbPKXt5bZjGY5n">Reactiflux Chat</a>
<a href="/react-native/versions.html" target="_blank">Latest Releases</a>
<a href="https://productpains.com/product/react-native/" target="_blank">Feature Requests</a>
<a href="https://react-native.canny.io/feature-requests" target="_blank">Feature Requests</a>
</div>
<div>
<h5>More</h5>
+34 -16
View File
@@ -12,15 +12,16 @@
'use strict';
var DocsSidebar = require('DocsSidebar');
var Footer = require('Footer');
var Header = require('Header');
var HeaderWithGithub = require('HeaderWithGithub');
var Footer = require('Footer');
var Marked = require('Marked');
var Metadata = require('Metadata');
var Prism = require('Prism');
var React = require('React');
var Site = require('Site');
var slugify = require('slugify');
var Metadata = require('Metadata');
var styleReferencePattern = /^[^.]+\.propTypes\.style$/;
@@ -33,6 +34,18 @@ function renderEnumValue(value) {
}
function renderType(type) {
const baseType = renderBaseType(type);
return type.nullable ? <span>?{baseType}</span> : baseType;
}
function spanJoinMapper(elements, callback, separator) {
return <span>{elements.map((rawElement, ii) => {
const el = callback(rawElement);
return (ii + 1 < elements.length) ? <span>{el}{separator}</span> : el;
})}</span>;
}
function renderBaseType(type) {
if (type.name === 'enum') {
if (typeof type.value === 'string') {
return type.value;
@@ -48,14 +61,18 @@ function renderType(type) {
}
if (type.name === 'shape') {
return '{' + Object.keys(type.value).map((key => key + ': ' + renderType(type.value[key]))).join(', ') + '}';
return <span>{'{'}{spanJoinMapper(
Object.keys(type.value),
(key) => <span>{key + ': '}{renderType(type.value[key])}</span>,
', '
)}{'}'}</span>;
}
if (type.name === 'union') {
if (type.value) {
return type.value.map(renderType).join(', ');
return spanJoinMapper(type.value, renderType, ', ');
}
return type.elements.map(renderType).join(' | ');
return spanJoinMapper(type.elements, renderType, ' | ');
}
if (type.name === 'arrayOf') {
@@ -92,7 +109,7 @@ function renderType(type) {
return type.raw;
}
return type.name;
return type.raw || type.name;
}
function renderTypeNameLink(typeName, docPath, namedTypes) {
@@ -121,7 +138,7 @@ function renderTypeWithLinks(type, docTitle, namedTypes) {
<div>
{
type.names.map((typeName, index, array) => {
let separator = index < array.length - 1 && ' | ';
const separator = index < array.length - 1 && ' | ';
return (
<span key={index}>
{renderTypeNameLink(typeName, docPath, namedTypes)}
@@ -176,7 +193,7 @@ function removeCommentsFromDocblock(docblock) {
}
function getNamedTypes(typedefs) {
let namedTypes = {};
const namedTypes = {};
typedefs && typedefs.forEach(typedef => {
if (typedef.name) {
const type = typedef.name.toLowerCase();
@@ -195,9 +212,9 @@ var ComponentDoc = React.createClass({
<span className="platform">{platform}</span>
)}
{name}
{' '}
{prop.type && <span className="propType">
{renderType(prop.type)}
{prop.required ? ': ' : '?: '}
{(prop.type || prop.flowType) && <span className="propType">
{renderType(prop.flowType || prop.type)}
</span>}
</Header>
{prop.deprecationMessage && <div className="deprecated">
@@ -428,9 +445,9 @@ var APIDoc = React.createClass({
<div className="prop" key={property.name}>
<Header level={4} className="propTitle" toSlug={property.name}>
{property.name}
{property.type &&
{(property.type || property.flowType) &&
<span className="propType">
{': ' + renderType(property.type)}
{': ' + renderType(property.flowType || property.type)}
</span>
}
</Header>
@@ -654,13 +671,14 @@ var Method = React.createClass({
</span> || ''}
{this.props.name}
<span className="methodType">
({this.props.params && this.props.params.length && this.props.params
({(this.props.params && this.props.params.length && this.props.params
.map((param) => {
var res = param.name;
res += param.optional ? '?' : '';
param.type && param.type.names && (res += ': ' + param.type.names.join(', '));
return res;
})
.join(', ')})
.join(', ')) || ''})
{this.props.returns && ': ' + this.renderTypehint(this.props.returns.type)}
</span>
</Header>
@@ -805,7 +823,7 @@ var Modal = React.createClass({
<div className="modal-content">
<button className="modal-button-close">&times;</button>
<div className="center">
<iframe className="simulator" src={url} width="256" height="550" frameborder="0" scrolling="no"></iframe>
<iframe className="simulator" src={url} width="256" height="550" frameborder="0" scrolling="no" />
<p>Powered by <a target="_blank" href="https://appetize.io">appetize.io</a></p>
</div>
</div>
+1
View File
@@ -21,6 +21,7 @@
"jsdoc-api": "^1.1.0",
"jstransform": "11.0.3",
"mkdirp": "^0.5.1",
"node-sass-middleware": "^0.11.0",
"optimist": "0.6.0",
"react": "~0.13.0",
"react-docgen": "^2.9.0",
+8
View File
@@ -93,6 +93,7 @@ function buildFile(layout, metadata, rawContent) {
function execute() {
var DOCS_MD_DIR = '../docs/';
var BLOG_MD_DIR = '../blog/';
var CONFIG_JSON_DIR = '../';
glob.sync('src/react-native/docs/*.*').forEach(rmFile);
glob.sync('src/react-native/blog/*.*').forEach(rmFile);
@@ -164,6 +165,13 @@ function execute() {
metadatas.config[key] = process.env[key];
});
// load showcase apps into metadata
var showcaseApps = JSON.parse(fs.readFileSync(
path.basename(CONFIG_JSON_DIR + 'showcase.json'),
{encoding: 'utf8'}
));
metadatas.showcaseApps = showcaseApps;
fs.writeFileSync(
'core/metadata.js',
'/**\n' +
+21 -12
View File
@@ -1,3 +1,12 @@
/**
* Copyright (c) 2013-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.
*/
'use strict';
const docgen = require('react-docgen');
@@ -18,18 +27,18 @@ function stylePropTypeHandler(documentation, path) {
docgen.utils.getPropertyName(propertyPath) !== 'style') {
return;
}
let valuePath = docgen.utils.resolveToValue(propertyPath.get('value'));
const valuePath = docgen.utils.resolveToValue(propertyPath.get('value'));
// If it's a call to StyleSheetPropType, do stuff
if (valuePath.node.type !== 'CallExpression' ||
valuePath.node.callee.name !== 'StyleSheetPropType') {
return;
}
// Get type of style sheet
let styleSheetModule = docgen.utils.resolveToModule(
const styleSheetModule = docgen.utils.resolveToModule(
valuePath.get('arguments', 0)
);
if (styleSheetModule) {
let propDescriptor = documentation.getPropDescriptor('style');
const propDescriptor = documentation.getPropDescriptor('style');
propDescriptor.type = {name: 'stylesheet', value: styleSheetModule};
}
});
@@ -48,13 +57,13 @@ function deprecatedPropTypeHandler(documentation, path) {
// Checks for deprecatedPropType function and add deprecation info.
propTypesPath.get('properties').each(function(propertyPath) {
let valuePath = docgen.utils.resolveToValue(propertyPath.get('value'));
const valuePath = docgen.utils.resolveToValue(propertyPath.get('value'));
// If it's a call to deprecatedPropType, do stuff
if (valuePath.node.type !== 'CallExpression' ||
valuePath.node.callee.name !== 'deprecatedPropType') {
return;
}
let propDescriptor = documentation.getPropDescriptor(
const propDescriptor = documentation.getPropDescriptor(
docgen.utils.getPropertyName(propertyPath)
);
// The 2nd argument of deprecatedPropType is the deprecation message.
@@ -78,7 +87,7 @@ function typedefHandler(documentation, path) {
// Name, type, description of the typedef
const name = declarationPath.value.id.name;
const type = { names: [typePath.node.id.name] };
const type = { names: [typePath.node.id ? typePath.node.id.name : typePath.node.type] };
const description = docgen.utils.docblock.getDocblock(path);
// Get the properties for the typedef
@@ -108,7 +117,7 @@ function typedefHandler(documentation, path) {
});
}
let typedef = {
const typedef = {
name: name,
description: description,
type: type,
@@ -134,7 +143,7 @@ function getTypeName(type) {
}
function jsDocFormatType(entities) {
let modEntities = entities;
const modEntities = entities;
if (entities) {
if (typeof entities === 'object' && entities.length) {
entities.map((entity, entityIndex) => {
@@ -155,7 +164,7 @@ function jsDocFormatHandler(documentation, path) {
if (!methods || methods.length === 0) {
return;
}
let modMethods = methods;
const modMethods = methods;
methods.map((method, methodIndex) => {
modMethods[methodIndex].params = jsDocFormatType(method.params);
modMethods[methodIndex].returns = jsDocFormatType(method.returns);
@@ -184,8 +193,8 @@ function findExportedObject(ast, recast) {
// handler.
// This converts any expression, e.g. `foo` to an object expression of
// the form `{propTypes: foo}`
let b = recast.types.builders;
let nt = recast.types.namedTypes;
const b = recast.types.builders;
const nt = recast.types.namedTypes;
let obj = objPath.node;
// Hack: This is converting calls like
@@ -215,7 +224,7 @@ function findExportedObject(ast, recast) {
}
function findExportedType(ast, recast) {
let types = recast.types.namedTypes;
const types = recast.types.namedTypes;
let definitions;
recast.visit(ast, {
visitExportNamedDeclaration: function(path) {
+5 -2
View File
@@ -13,12 +13,12 @@ const babel = require('babel-core');
const deepAssign = require('deep-assign');
const docgen = require('react-docgen');
const docgenHelpers = require('./docgenHelpers');
const docsList = require('./docsList');
const fs = require('fs');
const jsDocs = require('../jsdocs/jsdocs.js');
const jsdocApi = require('jsdoc-api');
const path = require('path');
const slugify = require('../core/slugify');
const docsList = require('./docsList');
const ANDROID_SUFFIX = 'android';
const CROSS_SUFFIX = 'cross';
@@ -200,8 +200,11 @@ function componentsToMarkdown(type, json, filepath, idx, styles) {
json.methods = json.methods.filter(filterMethods);
}
if (type === 'api') {
type = 'API';
}
// Put styles (e.g. Flexbox) into the API category
const category = (type === 'style' ? 'apis' : type + 's');
const category = (type === 'style' ? 'APIs' : type + 's');
const next = getNextComponent(idx);
const previous = getPreviousComponent(idx);
+26 -17
View File
@@ -7,27 +7,28 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
"use strict";
var connect = require('connect');
var http = require('http');
var optimist = require('optimist');
var path = require('path');
var reactMiddleware = require('react-page-middleware');
var convert = require('./convert.js');
'use strict';
const connect = require('connect');
const convert = require('./convert.js');
const http = require('http');
const optimist = require('optimist');
const path = require('path');
const reactMiddleware = require('react-page-middleware');
const sassMiddleware = require('node-sass-middleware');
var argv = optimist.argv;
const argv = optimist.argv;
var PROJECT_ROOT = path.resolve(__dirname, '..');
var FILE_SERVE_ROOT = path.join(PROJECT_ROOT, 'src');
const PROJECT_ROOT = path.resolve(__dirname, '..');
const FILE_SERVE_ROOT = path.join(PROJECT_ROOT, 'src');
var port = argv.port;
if (argv.$0 === 'node ./server/generate.js') {
let port = argv.port;
if (argv.$0.indexOf('node ./server/generate.js') !== -1) {
// Using a different port so that you can publish the website
// and keeping the server up at the same time.
port = 8079;
}
var buildOptions = {
const buildOptions = {
projectRoot: PROJECT_ROOT,
pageRouteRoot: FILE_SERVE_ROOT,
useBrowserBuiltins: false,
@@ -41,7 +42,15 @@ var buildOptions = {
static: true
};
var app = connect()
const app = connect()
.use(sassMiddleware({
/* Options */
src: path.join(PROJECT_ROOT,'styles'),
dest: path.join(FILE_SERVE_ROOT,'react-native','css'),
response: false,
outputStyle: 'extended',
prefix: '/react-native/css',
}))
.use(function(req, res, next) {
// convert all the md files on every request. This is not optimal
// but fast enough that we don't really need to care right now.
@@ -51,14 +60,14 @@ var app = connect()
next();
})
.use(reactMiddleware.provide(buildOptions))
.use(connect['static'](FILE_SERVE_ROOT))
.use(connect.static(FILE_SERVE_ROOT))
.use(connect.favicon(path.join(FILE_SERVE_ROOT, 'react-native', 'img', 'favicon.png')))
.use(connect.logger())
.use(connect.compress())
.use(connect.errorHandler());
var portToUse = port || 8079;
var server = http.createServer(app);
const portToUse = port || 8079;
const server = http.createServer(app);
server.listen(portToUse, function(){
console.log('Open http://localhost:' + portToUse + '/react-native/index.html');
});
+280
View File
@@ -0,0 +1,280 @@
[
{
"name": "Facebook",
"icon": "https://lh3.googleusercontent.com/ZZPdzvlpK9r_Df9C3M7j1rNRi7hhHRvPhlklJ3lfi5jk86Jd1s0Y5wcQ1QgbVaAP5Q=w300",
"linkAppStore": "https://itunes.apple.com/app/facebook/id284882215",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.facebook.katana&hl=en",
"infoLink": "https://code.facebook.com/posts/895897210527114/dive-into-react-native-performance/",
"infoTitle": "Using React Native in the Facebook App",
"pinned": true
},
{
"name": "Facebook Ads Manager",
"icon": "http://is5.mzstatic.com/image/pf/us/r30/Purple5/v4/9e/16/86/9e1686ef-cc55-805a-c977-538ddb5e6832/mzl.gqbhwitj.png",
"linkAppStore": "https://itunes.apple.com/us/app/facebook-ads-manager/id964397083?mt=8",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.facebook.adsmanager",
"infoLink": "https://code.facebook.com/posts/1189117404435352/react-native-for-android-how-we-built-the-first-cross-platform-react-native-app/",
"infoTitle": "How We Built the First Cross-Platform React Native App",
"pinned": true
},
{
"name": "Instagram",
"icon": "http://a4.mzstatic.com/us/r30/Purple62/v4/1f/8d/f9/1f8df910-8ec7-3b8e-0104-d44e869f4d65/icon175x175.jpeg",
"linkAppStore": "https://itunes.apple.com/app/instagram/id389801252?pt=428156&ct=igweb.unifiedHome.badge&mt=8",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.instagram.android&referrer=utm_source%3Dinstagramweb%26utm_campaign%3DunifiedHome%26utm_medium%3Dbadge",
"infoLink": "https://engineering.instagram.com/react-native-at-instagram-dd828a9a90c7#.3h4wir4zr",
"infoTitle": "React Native at Instagram",
"pinned": true
},
{
"name": "Airbnb",
"icon": "https://a2.muscache.com/airbnb/static/icons/apple-touch-icon-180x180-bcbe0e3960cd084eb8eaf1353cf3c730.png",
"linkAppStore": "https://itunes.apple.com/us/app/airbnb/id401626263?mt=8&bev=1472279725_4ITWKWGX6KrmU6pT&utm_medium=web&utm_source=airbnb&_branch_match_id=307510898795870823",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.airbnb.android&hl=en&referrer=bev%3D1472279725_4ITWKWGX6KrmU6pT%26utm_medium%3Dweb%26utm_source%3Dairbnb",
"infoLink": "https://www.youtube.com/watch?v=tUfgQtmG3R0",
"infoTitle": "Hybrid React Native Apps at Airbnb",
"pinned": true
},
{
"name": "Walmart",
"icon": "http://is2.mzstatic.com/image/thumb/Purple111/v4/64/9f/20/649f2026-e968-0417-660c-e5ee6d7977ff/source/350x350bb.jpg",
"linkAppStore": "https://itunes.apple.com/us/app/walmart-app-shopping-savings/id338137227?mt=8",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.walmart.android&hl=en",
"infoLink": " https://medium.com/walmartlabs/react-native-at-walmartlabs-cdd140589560#.ueonqqloc",
"infoTitle": "React Native at Walmart Labs",
"pinned": true
},
{
"name": "Tesla",
"icon": "https://lh3.googleusercontent.com/b79jRmQK2sZABaN0xI2wIAFrEcBxs0CuDzNTmF088DGrJXbemaMNRvrhqH0St28J7CU=w300-rw",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.teslamotors.tesla",
"linkAppStore": "https://itunes.apple.com/us/app/tesla-motors/id582007913?mt=8",
"pinned": true
},
{
"name": "Tencent QQ",
"icon": "http://pp.myapp.com/ma_icon/0/icon_6633_1461768893/96",
"linkPlayStore": "http://android.myapp.com/myapp/detail.htm?apkName=com.tencent.mobileqq",
"infoLink": "https://www.tencent.com/en-us/system.html",
"infoTitle": "QQ is China's largest messaging platform, with over 829 million active accounts",
"pinned": true
},
{
"name": "Baidu Mobile (\u624b\u673a\u767e\u5ea6)",
"icon": "http://a3.mzstatic.com/us/r30/Purple62/v4/90/7c/9b/907c9b4e-556d-1a45-45d4-0ea801719abd/icon175x175.png",
"linkPlayStore": "http://shouji.baidu.com/software/9896302.html",
"linkAppStore": "https://itunes.apple.com/us/app/%E6%89%8B%E6%9C%BA%E7%99%BE%E5%BA%A6-%E7%99%BE%E5%BA%A6%E4%B8%80%E4%B8%8B%E4%BD%A0%E5%B0%B1%E5%BE%97%E5%88%B0/id382201985?mt=8",
"infoLink": "http://baike.baidu.com/link?url=TW8YhcVN4tO_Jz5VqMclCjGhf12EEqMD_TeVC6efe2REZlx80r6T0dX96hdmNl36XogLyExXzrvFU9rFeqxg_K",
"infoTitle": "Baidu Mobile is a search engine used by over 600 million people in China",
"pinned": true
},
{
"name": "Bloomberg",
"icon": "http://is1.mzstatic.com/image/thumb/Purple71/v4/31/24/72/312472df-3d53-0acf-fc31-8a25682e528f/source/175x175bb.jpg",
"linkAppStore": "https://itunes.apple.com/us/app/bloomberg/id281941097?mt=8",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.bloomberg.android.plus&hl=en",
"infoLink": "https://www.techatbloomberg.com/blog/bloomberg-used-react-native-develop-new-consumer-app/",
"infoTitle": "How Bloomberg Used React Native to Develop its new Consumer App",
"pinned": true
},
{
"name": "Vogue",
"icon": "http://a2.mzstatic.com/us/r30/Purple30/v4/06/24/92/0624927f-a389-746c-27f9-e2466d59e55b/icon175x175.jpeg",
"linkAppStore": "https://itunes.apple.com/app/apple-store/id1087973225?pt=45076&ct=site-promo&mt=8",
"infoLink": "http://www.vogue.com/app",
"infoTitle": "",
"pinned": true
},
{
"name": "li.st",
"icon": "https://lh3.googleusercontent.com/tXt0HgJ7dCgOnuQ-lQr1P7E57mnOYfwXhRsV9lGcPwHPVvrDAN6YmpLVFgy88qKrkFI=w300",
"linkPlayStore": "https://play.google.com/store/apps/details?id=st.li.listapp",
"infoLink": "https://www.youtube.com/watch?v=cI9bDvDEsYE",
"infoTitle": "Building li.st for Android with React Native",
"pinned": true
},
{
"name": "Discord",
"icon": "http://a5.mzstatic.com/us/r30/Purple5/v4/c1/2f/4c/c12f4cba-1d9a-f6bf-2240-04085d3470ec/icon175x175.jpeg",
"linkAppStore": "https://itunes.apple.com/us/app/discord-chat-for-gamers/id985746746?mt=8",
"infoLink": "https://discord.engineering/react-native-deep-dive-91fd5e949933#.5jnqftgof",
"infoTitle": "Using React Native: One Year Later",
"pinned": true
},
{
"name": "Gyroscope",
"icon": "https://media.gyrosco.pe/images/magneto/180x180.png",
"linkAppStore": "https://itunes.apple.com/app/apple-store/id1104085053?pt=117927205&ct=website&mt=8",
"infoLink": "https://blog.gyrosco.pe/building-the-app-1dac1a97d253",
"infoTitle": "Building a visualization experience with React Native",
"pinned": true
},
{
"name": "Townske",
"icon": "http://a3.mzstatic.com/us/r30/Purple69/v4/8b/42/20/8b4220af-5165-91fd-0f05-014332df73ef/icon175x175.png",
"linkAppStore": "https://itunes.apple.com/us/app/townske-stunning-city-guides/id1018136179?ls=1&mt=8",
"infoLink": "https://hackernoon.com/townske-app-in-react-native-6ad557de7a7c",
"infoTitle": "The experience of a web developer building an app using React Native",
"pinned": true
},
{
"name": "Qzone (QQ\u7a7a\u95f4)",
"icon": "http://pp.myapp.com/ma_icon/0/icon_9959_1460036593/96",
"linkPlayStore": "http://android.myapp.com/myapp/detail.htm?apkName=com.qzone",
"infoLink": "https://en.wikipedia.org/wiki/Qzone",
"infoTitle": "Qzone is a Chinese social network with over 600 million users",
"pinned": false
},
{
"name": "QQ Music (QQ\u97f3\u4e50)",
"icon": "http://pp.myapp.com/ma_icon/0/icon_6259_1462429453/96",
"linkPlayStore": "http://android.myapp.com/myapp/detail.htm?apkName=com.tencent.qqmusic",
"infoLink": "http://www.wsj.com/articles/tencent-customers-come-for-the-music-stay-for-the-perks-1433869369",
"infoTitle": "Internet giant tries to get people to pay for digital music",
"pinned": false
},
{
"name": "Classroom (\u817e\u8baf\u8bfe\u5802)",
"icon": "http://pp.myapp.com/ma_icon/0/icon_10927178_1479093114/96",
"linkPlayStore": "http://android.myapp.com/myapp/detail.htm?apkName=com.tencent.edu",
"linkAppStore": "https://itunes.apple.com/cn/app/teng-xun-ke-tang-zhuan-ye/id931720936?mt=8",
"infoLink": "http://baike.baidu.com/view/13030839.htm",
"infoTitle": "Classroom is an education app by Chinese Internet giant Baidu",
"pinned": false
},
{
"name": "F8",
"icon": "https://raw.githubusercontent.com/fbsamples/f8app/master/ios/F8v2/Images.xcassets/AppIcon.appiconset/AppIcon%402x.png",
"infoLink": "http://makeitopen.com/tutorials/building-the-f8-app/planning/",
"infoTitle": "Tutorial: Building the F8 2016 conference app",
"pinned": false
},
{
"name": "Discovery VR",
"icon": "http://a2.mzstatic.com/us/r30/Purple6/v4/d1/d5/f4/d1d5f437-9f6b-b5aa-5fe7-47bd19f934bf/icon175x175.png",
"linkAppStore": "https://itunes.apple.com/us/app/discovery-vr/id1030815031?mt=8",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.discovery.DiscoveryVR",
"infoLink": "https://medium.com/ios-os-x-development/an-ios-developer-on-react-native-1f24786c29f0",
"infoTitle": "An iOS developer's experience building an app using React Native",
"pinned": false
},
{
"name": "Myntra",
"icon": "http://a5.mzstatic.com/us/r30/Purple6/v4/9c/78/df/9c78dfa6-0061-1af2-5026-3e1d5a073c94/icon350x350.png",
"linkAppStore": "https://itunes.apple.com/in/app/myntra-fashion-shopping-app/id907394059",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.myntra.android",
"infoLink": "https://www.youtube.com/watch?v=bXKr--rbewo",
"infoTitle": "React Native in Production at Scale",
"pinned": false
},
{
"name": "SoundCloud Pulse",
"icon": "https://i1.sndcdn.com/artworks-000149203716-k5je96-original.jpg",
"linkAppStore": "https://itunes.apple.com/us/app/soundcloud-pulse-for-creators/id1074278256?mt=8",
"infoLink": "https://developers.soundcloud.com/blog/react-native-at-soundcloud",
"infoTitle": "Why React Native worked well for us",
"pinned": false
},
{
"name": "CBS Sports Franchise Football",
"icon": "http://a2.mzstatic.com/us/r30/Purple69/v4/7b/0c/a0/7b0ca007-885a-7cfc-9fa2-2ec4394c2ecc/icon175x175.png",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.cbssports.fantasy.franchisefootball2015",
"infoLink": "http://www.cbssports.com/fantasy/football/games/franchise/2015",
"infoTitle": "Award winning Fantasy Football league manager built with React Native",
"pinned": false
},
{
"name": "Artsy",
"icon": "https://raw.githubusercontent.com/artsy/eigen/master/Artsy/Resources/Images.xcassets/AppIcon.appiconset/AppIcon167.png",
"linkAppStore": "https://itunes.apple.com/us/app/artsy-collect-bid-on-fine/id703796080?mt=8",
"infoLink": "https://artsy.github.io/series/react-native-at-artsy/",
"infoTitle": "React Native at Artsy",
"pinned": false
},
{
"name": "Huiseoul (\u60e0\u9996\u5c14)",
"icon": "https://cdn.huiseoul.com/icon.png",
"linkAppStore": "https://itunes.apple.com/us/app/hui-shou-er-ni-si-ren-mei/id1127150360?ls=1&mt=8",
"infoLink": "https://engineering.huiseoul.com/building-a-conversational-e-commerce-app-in-6-weeks-with-react-native-c35d46637e07",
"infoTitle": "Building a conversational E-commerce app in 6 weeks with React Native",
"pinned": false
},
{
"name": "Sleeperbot",
"icon": "https://blitzchat.net/uploads/c8425332190a4f4b852d7770ad32e602/original.png",
"linkAppStore": "https://itunes.apple.com/us/app/sleeperbot-fantasy-football/id987367543?mt=8",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.sleeperbot&hl=en",
"infoLink": "https://medium.com/sleeperbot-hq/switching-to-react-native-in-production-on-ios-and-android-e6b675402712#.cug6h6qhn",
"infoTitle": "Switching to React Native in Production on iOS and Android",
"pinned": false
},
{
"name": "JD\uff08\u624b\u673a\u4eac\u4e1c\uff09",
"icon": "https://lh3.googleusercontent.com/AIIAZsqyEG0KmCFruh1Ec374-2l7n1rfv_LG5RWjdAZOzUBCu-5MRqdLbzJfBnOdSFg=w300-rw",
"linkAppStore": "https://itunes.apple.com/cn/app/shou-ji-jing-dong-xin-ren/id414245413?mt=8",
"linkPlayStore": "https://app.jd.com/android.html",
"infoLink": "http://ir.jd.com/phoenix.zhtml?c=253315&p=irol-homeProfile",
"infoTitle": "JD.com is China\u2019s largest ecommerce company by revenue and a member of the Fortune Global 500.",
"pinned": false
},
{
"name": "Chop",
"icon": "https://pbs.twimg.com/profile_images/656536498951446529/6zU6BvgB.png",
"linkAppStore": "http://apple.co/2dfkYH9",
"infoLink": "http://blog.getchop.io/2016/10/13/how-we-built-chop/",
"infoTitle": "How we built Chop using React Native",
"pinned": false
},
{
"name": "Blink",
"icon": "https://lh3.googleusercontent.com/QaId7rFtOjAT-2tHVkKB4lebX_w4ujWiO7ZIDe3Hd99TfBmPmiZySbLbVJV65qs0ViM=w300-rw",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.witapp",
"infoLink": "https://hashnode.com/post/what-we-learned-after-using-react-native-for-a-year-civdr8zv6058l3853wqud7hqp",
"infoTitle": "What we learned after using React Native for a year",
"pinned": false
},
{
"name": "Delivery.com",
"icon": "https://lh3.googleusercontent.com/ZwwQHQns9Ut2-LqbMqPcmQrsWBh3YbmbIzeDthfdavw99Ziq0unJ6EHUw8bstXUIpg=w300-rw",
"linkAppStore": "https://itunes.apple.com/us/app/delivery.com-food-alcohol/id435168129?mt=8",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.deliverycom&hl=en",
"infoLink": "https://medium.com/delivery-com-engineering/react-native-in-an-existing-ios-app-delivered-874ba95a3c52#.37qruw6ck",
"infoTitle": "React Native in an Existing iOS App: Getting Started",
"pinned": false
},
{
"name": "Yeti Smart Home",
"icon": "https://res.cloudinary.com/netbeast/image/upload/v1484303676/Android_192_loykto.png",
"linkAppStore": "https://itunes.apple.com/us/app/yeti-smart-home/id1190638808?mt=8",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.netbeast.yeti",
"infoLink": "https://medium.com/@jesusdario/developing-beyond-the-screen-9af812b96724#.ozx0xy4lv",
"infoTitle": "How React Native is helping us to reinvent the wheel",
"pinned": false
},
{
"name": "Jack",
"icon": "https://s3-eu-west-1.amazonaws.com/jack-public/react-native-showcase/jack-raw-icon.png",
"linkAppStore": "https://itunes.apple.com/us/app/you-have-a-jack/id1019167559?ls=1&mt=8",
"linkPlayStore": "https://play.google.com/store/apps/details?id=com.jack45.jack",
"infoLink": "https://medium.com/@herdani/our-switch-to-react-native-f4ada19f0f3d#.ogwjzf2tw",
"infoTitle": "Our switch to React Native",
"pinned": false
},
{
"name": "Causr",
"icon": "http://is2.mzstatic.com/image/thumb/Purple111/v4/9d/14/20/9d142015-3319-f613-2886-ad889609466a/source/175x175bb.jpg",
"linkAppStore": "https://itunes.apple.com/us/app/causr-business-networking/id1129819484",
"infoLink": "https://medium.com/causr/why-we-chose-react-native-abd7d58a18b5",
"infoTitle": "Why we chose React Native",
"pinned": false
},
{
"name": "Flare by GoDaddy",
"icon": "http://x.co/FlareIcon",
"linkAppStore": "http://x.co/Flare",
"linkPlayStore": "http://x.co/FlareAndr",
"infoLink": "http://x.co/FlareNews",
"infoTitle": "Social network that connects entrepreneurs to fellow entrepreneurs, consumers, investors and experts",
"pinned": false
}
]

Some files were not shown because too many files have changed in this diff Show More