React Native - 0.60
06 Sep 2019- https://facebook.github.io/react-native/blog/2019/07/03/version-60
- https://github.com/react-native-community/releases/blob/master/CHANGELOG.md#060
[iOS and Android] CLI autolinking
https://facebook.github.io/react-native/blog/2019/07/03/version-60#native-modules-are-now-autolinked
The team working on the React Native CLI has introduced major improvements to native module linking called autolinking! Most scenarios will not require the use of react-native link anymore. At the same time, the team overhauled the linking process in general. Be sure to
react-native unlink
any preexisting dependencies as mentioned in the docs above.
=> now there’s no need to link libraries manually - it’s done automatically when building application.
for iOS remove all application pods from ios/Podfile (BVLinearGradient
,
Picker
, RNCAsyncStorage
, etc.) and run pod install
- these RN module pods
must be detected automatically.
however this is not the case with OneSignalNotificationServiceExtension
extension - I still had to link it manually in Xcode (select corresponding
target and add libRCTOneSignal.a in Link Binary With Libraries
section).
not all packages support autolinking - first of all this concerns outdated
packages which haven’t been updated for a while. in case of iOS they might have
no podspec => link such packages as usual - using react-native link
:
$ react-native link react-native-admob
[Android] AndroidX
It was June 17th, 2019, Android released their new package names for their DEPRECATED support libraries + other tools inside AndroidX…
Basically, all of the basic libraries that Google provide for the Android + React Native Community, on the last or close to last versions were affected. In some cases, it was the fault of the react-native libraries maintainers for using imports like + symbols that are ambiguous and generally take the last version available of the library and in some other cases was caused by the fact that the developer was actually using new features from the latest release.
https://stackoverflow.com/a/52517772/3632318
AndroidX is redesigned library to make package names more clear.
https://developer.android.com/jetpack/androidx/
AndroidX is a major improvement to the original Android Support Library.
AndroidX fully replaces the Support Library by providing feature parity and new libraries.
https://developer.android.com/jetpack/androidx/migrate
AndroidX replaces the original support library APIs with packages in the androidx namespace. Only the package and Maven artifact names changed; class, method, and field names did not change.
https://stackoverflow.com/a/54533702/3632318
One app should use either AndroidX or old Android Support libraries. That’s why you faced this issue.
So the solution is to use either AndroidX or old Support Library.
so the problem is that application might be using both new AndroidX and old Android Support libraries which is not allowed.
it’s possible to examine all application dependencies using this command:
$ cd android
$ ./gradlew app:dependencies
AFAIU there must be either com.android.support
or androidx.*
libraries in
the output but not both.
the reason why AndroidX libraries leak into current application dependencies is
that many RN libraries use the latest versions of Google libraries instead of
specific ones, say (note +
symbol):
// https://github.com/zo0r/react-native-push-notification/blob/c4c961cf3a76bffe4e9e36cf00201611eecbb898/android/build.gradle
dependencies {
// ...
implementation "com.google.android.gms:play-services-gcm:${safeExtGet('googlePlayServicesVersion', '+')}"
// ...
implementation "com.google.firebase:firebase-messaging:${safeExtGet('firebaseVersion', '+')}"
}
but the latest versions of Google started to depend on AndroidX libraries since June 17th, 2019 and it was a breaking change.
there are 2 approaches to solve this problem:
- use Android Support Libraries only
- migrate everything to AndroidX
use Android Support Libraries only
in most cases it’s just necessary to use specific versions of Google libraries which don’t depend on AndroidX libraries.
this can be enforced in several ways:
-
[RECOMMENDED] try upgrading RN packages
maintainers of RN packages might have fixed their build.gradle files to use specific versions of Google libraries (without AndroidX dependencies):
$ yarn upgrade --pattern react-native
it was the case with
react-native-device-info
package:https://github.com/react-native-community/react-native-device-info/pull/693
With the latest firebase/gcm release, the latest version (+) now brings in androidx dependencies. react-native won’t be ready for androidx until 0.60 so constrain the gcm version to 16.1.0 (the latest pre-androidx version).
# https://github.com/react-native-community/react-native-device-info/pull/693/files#diff-7ae5a9093507568eabbf35c3b0665732 dependencies { implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}" - implementation "com.google.android.gms:play-services-gcm:${safeExtGet('googlePlayServicesVersion', '+')}" + implementation "com.google.android.gms:play-services-gcm:${safeExtGet('googlePlayServicesVersion', '16.1.0')}"
-
fork RN packages
if RN package is not actively maintained it might take a long time till it’s fixed, so it makes sense to fork and fix it by yourself (just like it’s done in the linked PR which is also mentioned previously).
-
force use of specific versions of Google libraries
- https://github.com/facebook/react-native/issues/25292#issuecomment-502998885
- https://medium.com/@suchydan/how-to-solve-google-play-services-version-collision-in-gradle-dependencies-ef086ae5c75f
- https://github.com/facebook/react-native/issues/25307#issuecomment-503516458
- https://medium.com/mindorks/avoiding-conflicts-in-android-gradle-dependencies-28e4200ca235
in this solution it’s not necessary to modify Google libraries but your own android/app/build.gradle file only.
perform these steps for each RN package which depends on AndroidX libraries until there are no AndroidX dependencies (run
gradlew app:dependencies
to find such RN packages as described above):-
find Google libraries in RN package dependencies
libraries inside
com.google.android.gms
andcom.google.firebase
groups are affected:// https://github.com/zo0r/react-native-push-notification/blob/c4c961cf3a76bffe4e9e36cf00201611eecbb898/android/build.gradle dependencies { // ... implementation "com.google.android.gms:play-services-gcm:${safeExtGet('googlePlayServicesVersion', '+')}" // ... implementation "com.google.firebase:firebase-messaging:${safeExtGet('firebaseVersion', '+')}" }
-
exclude those libraries from RN package dependencies in your application
// android/app/build.gradle dependencies { - implementation project(':react-native-push-notification') + implementation(project(':react-native-push-notification')) { + exclude group: 'com.google.android.gms', module: 'play-services-gcm' + exclude group: 'com.google.firebase', module: 'firebase-messaging' + }
-
use specific versions of those libraries in your application
// android/app/build.gradle dependencies { // ... + implementation("com.google.android.gms:play-services-base:16.1.0") { + force = true; + } + implementation("com.google.android.gms:play-services-basement:16.2.0") { + force = true; + } + implementation("com.google.android.gms:play-services-gcm:16.1.0") { + force = true; + } + implementation("com.google.android.gms:play-services-stats:16.0.1") { + force = true; + } + implementation("com.google.firebase:firebase-messaging:18.0.0") { + force = true; + } + implementation("com.google.firebase:firebase-iid:18.0.0") { + force = true; + } }
a rule of thumb is not to use June versions, the last but one versions will do in most cases.
see Maven repository for the list of library versions and corresponding release dates.
it might be necessary to force versions of both direct and transitive RN package dependencies (just like in example above) - inspect the output of
gradlew app:dependencies
command after adding all direct dependencies.
migrate everything to AndroidX
- https://facebook.github.io/react-native/blog/2019/07/03/version-60#androidx-support
- https://github.com/react-native-community/react-native-camera/blob/master/docs/AndroidXMigration.md
-
Multidex support
// android/app/build.gradle dependencies { // ... - implementation 'com.android.support:multidex:1.0.3' + + def multidex_version = "2.0.1" + implementation 'androidx.multidex:multidex:$multidex_version'
-
Jetifier
Jetifier is run automatically when running application with
react-native
:$ react-native run-android ... info Running jetifier to migrate libraries to AndroidX. You can disable it using "--no-jetifier" flag. Jetifier found 1220 file(s) to forward-jetify. Using 4 workers...
however it’s not the case when building Android release with Gradlew - the only solution I’ve found is to run Jetifier manually before building release (or add some Gradlew task to automate it):
$ npx jetify $ alias build_apk='cd android && ./gradlew assembleRelease; cd ..' $ build_apk
deprecated lifecycle hooks
https://stackoverflow.com/a/51981144/3632318
componentWillReceiveProps is a synchronous hook. Calling asynchronous function like data fetching inside this hook will need to render in between when the new props are set and when data has finished loading.
Which won’t give you unnecessary renders. Note that getDerivedStateFromProps is used only in rare case though. So, I suggest you to use componentDidUpdate hook as far as possible.
The similar things happen when comparing componentWillMount and componentDidMount. Use componentDidMount whenever you need operate async operation and forget componentWillMount at all condition.
componentWillReceiveProps
https://reactjs.org/docs/react-component.html#componentdidupdate
You may call setState() immediately in componentDidUpdate() but note that it must be wrapped in a condition like in the example above, or you’ll cause an infinite loop.
https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#side-effects-on-props-change
Instead, the componentDidUpdate lifecycle should be used since it is guaranteed to be invoked only once per update
- componentWillReceiveProps(nextProps) {
- if (nextProps.gamer !== this.props.gamer) {
- this.setState({comment: nextProps.gamer.game_comment});
+ componentDidUpdate(prevProps) {
+ if (this.props.gamer !== prevProps.gamer) {
+ this.setState({comment: this.props.gamer.game_comment});
}
}
componentWillMount
https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#fetching-external-data
There is a common misconception that fetching in componentWillMount lets you avoid the first empty rendering state. In practice this was never true because React has always executed render immediately after componentWillMount. If the data is not available by the time componentWillMount fires, the first render will still show a loading state regardless of where you initiate the fetch. This is why moving the fetch to componentDidMount has no perceptible effect in the vast majority of cases.
- componentWillMount() {
+ componentDidMount() {
this.props.getGameStat().catch(AlertHelpers.serverError);
}