mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
113ebb3254 | ||
|
|
1a7e85e03e | ||
|
|
d2c63e4998 | ||
|
|
5f01aa78de | ||
|
|
d873f52574 | ||
|
|
f78ec778ce | ||
|
|
6ebd6bca5a | ||
|
|
1b7de4f676 | ||
|
|
359dc58b33 | ||
|
|
dbe978a40a | ||
|
|
04ef14f7d2 | ||
|
|
ca099814ae | ||
|
|
b224414262 | ||
|
|
6e936f34ef | ||
|
|
4c162e25c9 | ||
|
|
a44c5e6b5c | ||
|
|
7460169372 | ||
|
|
969d8c4be1 | ||
|
|
e55fcc5a4b | ||
|
|
879d1c1dba | ||
|
|
5fc656dc12 | ||
|
|
41230fe352 | ||
|
|
98aea8cdf0 | ||
|
|
cd8b6ff5ac | ||
|
|
7d09e501b5 | ||
|
|
de8ec5afd9 | ||
|
|
fce17ed847 | ||
|
|
26aa66091d | ||
|
|
3b940301f3 | ||
|
|
8b4ade2c6b | ||
|
|
b3e0d6e89a | ||
|
|
66bb79a773 | ||
|
|
6e650937fd | ||
|
|
852775fd1a | ||
|
|
3ccf8dfb09 | ||
|
|
d877c25cc4 | ||
|
|
9c2760ae20 | ||
|
|
2981560e40 | ||
|
|
ead6dcd092 | ||
|
|
363b904d59 | ||
|
|
021e754c8f | ||
|
|
2bc2ae761a | ||
|
|
3a6dff4f4f | ||
|
|
ff8257c47e | ||
|
|
0ec85db3bf | ||
|
|
26060a4526 |
@@ -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
|
||||
* >
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 doesn’t already exist before opening a new issue. It’s helpful if you include the version of React Native and OS you’re 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
@@ -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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
VERSION_NAME=1000.0.0-master
|
||||
VERSION_NAME=0.42.3
|
||||
GROUP=com.facebook.react
|
||||
|
||||
POM_NAME=ReactNative
|
||||
|
||||
@@ -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 we’re 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
@@ -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
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||

|
||||
|
||||
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).
|
||||
@@ -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
|
||||
|
||||
@@ -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 @@
|
||||
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()})()
|
||||
|
||||
@@ -5,7 +5,7 @@ layout: docs
|
||||
category: Guides
|
||||
permalink: docs/accessibility.html
|
||||
next: timers
|
||||
previous: animations
|
||||
previous: debugging
|
||||
---
|
||||
|
||||
## Native App Accessibility (iOS and Android)
|
||||
|
||||
@@ -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
|
||||
---
|
||||
|
||||
@@ -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:
|
||||
|
||||

|
||||
|
||||
If your trace .html file isn't opening correctly, check your browser console for the following:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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`:
|
||||
|
||||

|
||||
|
||||
### 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:
|
||||
|
||||

|
||||
|
||||
### 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`:
|
||||
|
||||

|
||||
|
||||
### 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`:
|
||||
|
||||

|
||||
|
||||
## Identifying a culprit
|
||||
|
||||
A smooth animation should look something like the following:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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
@@ -4,7 +4,7 @@ title: Animations
|
||||
layout: docs
|
||||
category: Guides
|
||||
permalink: docs/animations.html
|
||||
next: accessibility
|
||||
next: navigation
|
||||
previous: handling-touches
|
||||
---
|
||||
|
||||
|
||||
+30
-15
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||

|
||||
|
||||
<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.
|
||||

|
||||
|
||||
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.
|
||||

|
||||
|
||||
> 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.
|
||||
|
||||

|
||||
|
||||
`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.
|
||||
|
||||

|
||||
|
||||
`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.
|
||||

|
||||
|
||||
<block class="windows linux android" />
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
---
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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'.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
---
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
---
|
||||
|
||||
@@ -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,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,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
@@ -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.
|
||||
|
||||

|
||||
|
||||
`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
@@ -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
@@ -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
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
> **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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
>
|
||||
> 
|
||||
>
|
||||
> 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`:
|
||||
|
||||

|
||||
|
||||
- **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:
|
||||
|
||||

|
||||
|
||||
- **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`:
|
||||
|
||||

|
||||
|
||||
- **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`:
|
||||
|
||||

|
||||
|
||||
#### Identifying a culprit
|
||||
|
||||
A smooth animation should look something like the following:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||
@@ -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,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).
|
||||
|
||||
|
||||
@@ -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
|
||||
---
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -4,7 +4,7 @@ title: Timers
|
||||
layout: docs
|
||||
category: Guides
|
||||
permalink: docs/timers.html
|
||||
next: direct-manipulation
|
||||
next: javascript-environment
|
||||
previous: accessibility
|
||||
---
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
---
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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')} />
|
||||
|
||||
@@ -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.
|
||||
|
||||

|
||||
|
||||
### 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).
|
||||
@@ -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'),
|
||||
|
||||
@@ -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(),
|
||||
},
|
||||
|
||||
@@ -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
@@ -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...');
|
||||
|
||||
@@ -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,
|
||||
|
||||
+7
-13
@@ -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>
|
||||
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+4
-3
@@ -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
@@ -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 */
|
||||
|
||||
Executable
+62
@@ -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
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
|
||||
@@ -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">×</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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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' +
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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');
|
||||
});
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user