Skip to content

Android - Add a ReactFragment - #12199

Closed
jpshelley wants to merge 16 commits into
react:masterfrom
jpshelley:Android-CreateReactFragment
Closed

jpshelley wants to merge 16 commits into
react:masterfrom
jpshelley:Android-CreateReactFragment

Conversation

@jpshelley

@jpshelley jpshelley commented Feb 3, 2017 •

Copy link
Copy Markdown
Contributor

Overview

React Native on Android has currently been focused and targeted at using an Activity for its main form of instantiation.

While this has probably worked for most companies and developers, you lose some of the modularity of a more cohesive application when working in a "brown-field" project that is currently native. This hurts more companies that are looking to adopt React Native and slowly implement it in a fully native application.

A lot of developers follow Android's guidelines of using Fragments in their projects, even if it is a debated subject in the Android community, and this addition will allow others to embrace React Native more freely. (I even assume it could help with managing navigation state in applications that contain a decent amount of Native code and would be appreciated in those projects. Such as sharing the Toolbar, TabBar, ViewPager, etc in Native Android)

Even with this addition, a developer will still need to host the fragment in an activity, but now that activity can contain native logic like a Drawer, Tabs, ViewPager, etc.

Test plan (required)

  • We have been using this class at Hudl for over a couple of months and have found it valuable.
  • If the community agrees on the addition, I can add documentation to the Android sections to include notes about the potential of this Fragment.
  • If the community agrees on the addition, I can update one or more of the examples in the /Examples folder and make use of the Fragment, or even create a new example that uses a native layout manager like Drawer, Tabs, Viewpager, etc)

Make sure tests pass on both Travis and Circle CI.

To Note:

Release Notes

[ANDROID][FEATURE][ReactAndroid/src/main/java/com/facebook/react/ReactFragment.java] - Adds support for Android's Fragment system. This allows for a more hybrid application.

@facebook-github-bot facebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. GH Review: review-needed labels Feb 3, 2017
Fix typo and remove TODO
Comment thread ReactAndroid/src/main/java/com/facebook/react/ReactFragment.java

public class ReactFragment extends Fragment implements PermissionAwareActivity, DefaultHardwareBackBtnHandler {

public final static int REQUEST_OVERLAY_CODE = 1111;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? Can you add a comment please?

@jpshelley jpshelley Feb 6, 2017 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, its basically a duplication of the [ReactActivityDelegate] (https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java#L34) which relies on the permission system to allow dev/debug mode.

Edit: I can add comments, or as mentioned in your summary comment I can attempt to share the code between this and the delegate class?

private static final String REDBOX_PERMISSION_MESSAGE =
"Overlay permissions need to be granted in order for react native apps to run in dev mode.";
private static final String REDBOX_PERMISSION_GRANTED_MESSAGE =
"Overlay permissions have been granted.";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these needed? Can you add a comment?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above ^^

Comment thread ReactAndroid/src/main/java/com/facebook/react/ReactFragment.java Outdated
Comment thread ReactAndroid/src/main/java/com/facebook/react/ReactFragment.java Outdated
handled = true;
}
}
return handled;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like copy & paste? Can you extract it to a shared helper rather than copy & pasting?

@mkonicek

mkonicek commented Feb 6, 2017 •

Copy link
Copy Markdown
Contributor

The need for using RN in fragments is understandable. The idea seems good to me.

However, how much of the code here is copied from somewhere else in the codebase? It's going to be very hard to maintain the exact same copy of the logic in two different places - it's guaranteed people will only update one place and forget to update the other. If we want to commit this, every non-trivial piece of logic that's been copied from somewhere will need to be extracted to a shared helper (e.g. a class with helpers that's used by both the activity and fragment).

Looks like the code doesn't compile?
https://circleci.com/gh/facebook/react-native/15316?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link

@jpshelley

Copy link
Copy Markdown
Contributor Author

@mkonicek great insight and thanks for reviewing the code!

  • Code doesn't compile because I used android annotations (nullable, non-null, etc) I can remove those and should compile
  • I agree with the duplication, I think it hints at the code review comments you left too. This is basically a duplicate of the ReactActivityDelegate for a Fragment, I can attempt to share more of the code between the two. HOWEVER, a Fragment will always depend on its Activity, and therefor will need some of the callbacks and maybe an updated example or documentation will help?

…ctFragment

* upstream/master: (23 commits)
  packager: fix uncaught rejection
  `[-RCTUIManager setFrame:forView:]` was replaced with `[-RCTUIManager setSize:forView:]`
  Allow specific border width styling
  Added property display: flex and none
  Add border support to ScrollViews
  refactor Module.js
  Random @providesModule related fixes
  Android: Add onScroll event to TextInput
  Support the --remote option in bump-oss-version.js script
  packager: reestablish changes reverted by d63f9c
  Forcing git diff to not use colorized output
  Added missing files to React.xcodeproj
  removed bestander from email notifications
  Add perspective transform support on Android
  Better ListView - FlatList
  Rename files to match @providesModule annotations
  Fixed crash in RCTScrollView on iOS 8
  Fix <SwipeableRow /> layout.
  Eject CLI command to re-create native folders
  Fix NetworkingModule losing Cookies when multiple CatalystInstances exist and one is destroyed
  ...
I'm currently not to sold on the re-use of this commit, but wanted to
take it a step forward and see if anything good could come out of it. I
welcome strict critticism and hope to improve this code. I still think a
Fragment would be valuable, but sharing the necessary methods between
the ReactActivity and making sure to point out methods that can't be
shared is required.
@jpshelley

Copy link
Copy Markdown
Contributor Author

@mkonicek I've added a commit to fix this build failures, as well as attempt to re-use the existing code from the ReactActivityDelegate. I didn't want to eliminate the Delegate completely, so I created a new root delegate that ReactActivityDelegate calls into. I'm not sold on the current implementation but wanted to get something pushed so we could communicate potential changes and improvements.

I foresee something like the following:
react-delegate 1

The benefit of the Activity is less native code and easier to get a "green-field" project started.
The benefit of the Fragment is easier to integrate with an existing "brown-field" project and make use of native ViewGroups/Activities.

@serj-lotutovici serj-lotutovici left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something I was defiantly missing. Hoping to see this in a stable version.

One side note. The consuming activity needs to override at least two methods, which probably should be documented somewhere.


public void onHostResume() {
if (getReactNativeHost().hasInstance()) {
if (mActivity instanceof DefaultHardwareBackBtnHandler) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This forces the host activity to implement the interface, when in fact it's already implemented by the ReactFragment (that btw is never used).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call out. How would you see this working then? Extra param in the constructor to pass a DefaultHardwareBackBtnHandler object which can either be a fragment or an activity that implements the interface?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be my first choice. I wonder if the the behaviour would be the same, since ReactActivity is calling super.onBackPressed() and the fragment only calls getActivity().onBackPressed(). The chain of thought is that the host activity will have to call the fragment's onBackPressed method do decide whether or not to call the super method.

@jpshelley jpshelley Feb 16, 2017 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually implementing react-navigation this week in a project that will make use of the back button login, in this exact fragment class, so I can tweak it a bit to figure out what feels best. But I think I agree with what you're saying above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@serj-lotutovici So I updated the code a bit in b089982

After messing with fragment navigations stacks this week, it seemed easier to allow the Activity to handle the back navigation. If the Fragment handled the back button handler, it ended up getting stuck in a loop, or it wasn't able to safely call the super.onBackPressed() like I was hoping for. I'm open to suggestions but this is the conclusion I came to.

This will be shown via documentation for sure, but I'll work on documentation if/when we get this PR "approved" and the idea is solidified.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jpshelley I agree on the importance of Fragment support. For example some components like ViewPager return Fragments for each view. This is standard. Btw, are there tests for the Fragment solution or do you have an example app that is using these modifications?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are currently in the process of open sourcing our solution at Hudl. Since contributing back to React Native seems to be taking longer then expected, we wanted to be able to share our approach with others. I will comment back on this PR when that happens.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds great! I am currently doing React Native component integration into Native app and would like to try it out when available.

…ctFragment

* upstream/master: (148 commits)
  Match on `s.version` only
  API update and bug fixes
  Clean up separator stuff
  Improve flow typing
  Remove unused RCTMessageQueue
  Make flow types update to date with proptypes with ImageSource
  Move some utilities to RCTCxxUtils
  Fail safely if there are extra DrawView commands in ClippingDrawCommandManager
  Deleting Optimized Bundle!
  Support QuickPerformanceLogger.js in the old bridge
  Revert D4559304: FBReactKit/js: upgrade babel dependencies
  Revert D4559167: packager: introducing async/await
  Removed duplicate code by calculating with mainSize/crossSize
  Fix up some small issues with YGUnitAuto
  Move JSBigString to its own header
  Move RCTFollyConvert to the correct namespace
  Change cursor color when using selectionColor on Android
  BREAKING: Change the js1 - packager - RN App protocol to version 2
  Allow listener for native Animated.event
  Update grammar and typos in documentation
  ...
* master:
  add Systrace to all PerformanceLogger.js markers
  Guard against missing native module
  Fix CI runs
  Move tryBytecode to bundle specifier
  Support QuickPerformanceLogger.js in CxxBridge
  Add missing reference image for tvOS ImageSnapshotTest
  Move initializedExecutorWithContextProvider API
@jpshelley

Copy link
Copy Markdown
Contributor Author

I've ironed out more of the ReactFragment this week, however I'm wondering if it is worth refactoring ReactActivityDelegate now while RN is still in a 0.X.Y stage and is able for "breaking changes"

The change I mention is similar to the idea I showed above via the diagram. The delegate will handle all common operations between a ReactFragment and ReactActivity such as dev tools, loading the app, and general interactions.

Let me know what people think about:

  • The PR as a whole currently
  • If the consensus is positive towards the question above, then where should the documentation live and should samples be updated to reflect a ReactFragment usage?
  • Finally thoughts on a more shared delegate across the two.

@jpshelley

Copy link
Copy Markdown
Contributor Author

Any thoughts on the build errors? Looks like a lot Flow related issue of SectionList which I believe was a recent commit.

@dobrienlzd

dobrienlzd commented May 10, 2017 •

Copy link
Copy Markdown

@jpshelley Is there any example code you can push to github or bitbucket using the ReactFragment? This would help in it's use and review.

@githubdoramon

Copy link
Copy Markdown

@jpshelley Any chance you can show a example code for using with a view pager?

I tried the solution at http://stackoverflow.com/questions/35221447/react-native-inside-a-fragment, but the fragment don`t detect touches to it, so I can't perform any actions with it.

Thank you

@jpshelley

Copy link
Copy Markdown
Contributor Author

For anyone following this PR, we are looking at open sourcing our solution at Hudl. From there we can add examples on how to include with ViewPagers or other Fragment based solutions.

@inakianduaga

Copy link
Copy Markdown

@jpshelley can you briefly list the instructions to get this working without waiting for a merge into the RN codebase (so moving all three classes into a separate namespace and adding it). I'm trying to get RN implemented in my current company and need to integrate it with an existing android app that uses fragments

@jpshelley

Copy link
Copy Markdown
Contributor Author

@cpojer @mdvacca Updated with master. Awesome to see the androidx support came in pretty early as well!

if (mPermissionListener != null && mPermissionListener.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
mPermissionListener = null;
}
mPermissionsCallback = args -> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you change this to not use a lambda expression? We are still supporting older versions of Java that don't have them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup definitely. Curious question, what version of Java do you back support and is there a specific reason why? Just wanting to learn from it.

@cpojer

cpojer commented Mar 20, 2019

Copy link
Copy Markdown
Contributor

@jpshelley that's great! @mdvacca will do a review soon and we can hopefully get this landed finally. In the meantime, can you get the android tests to pass on CI? One of the issues is that you are using lambda expressions which unfortunately we cannot use yet.

@@ -15,8 +15,6 @@

import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.Callback;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before looking to merge this - does it make sense to have a ReactActivityDelegate and then a ReactDelegate or does it make sense for ReactActivity to use the ReactDelegate directly? Was trying to bring smaller changes with this PR and focus primarily on the addition of the ReactFragment but also don't want to incur possible Tech Debt

@facebook-github-bot facebook-github-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mdvacca is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@react-native-bot

Copy link
Copy Markdown
Collaborator

This pull request was successfully merged by @jpshelley in 691f2f9.

When will my fix make it into a release? | Upcoming Releases

@react-native-bot react-native-bot added the Merged This PR has been merged. label Apr 4, 2019
@cpojer

cpojer commented Apr 4, 2019 •

Copy link
Copy Markdown
Contributor

OMG thank you @mdvacca for merging this PR. It's been open for over two years and I'm glad we could finally bring this functionality into React Native, especially since a ton of people have asked for it.

@jpshelley I also want to thank you for sticking with us for all this time and going through numerous reviews and rebases. I cannot imagine myself how demoralizing it must have been to wait for this long but I am incredibly thankful that you did. Since you sent this PR, the React Native team at Facebook changed completely and we try to spend more time on open source to bring your contributions into the repo faster. I hope to see many more PRs from you in the future, and I promise we'll get to them much faster. For now, would you be willing to add documentation for this functionality to the React Native website?

Finally, thanks @mkonicek for reviewing this PR a long time ago and helping so much on open source at FB! I know how much work it was and that it's easy to drown in it.

@mdvacca

mdvacca commented Apr 5, 2019

Copy link
Copy Markdown
Contributor

We reverted this commit because it caused a bug internally at Facebook. I will work on a fix and bring the code back in a commit soon.

@jpshelley

Copy link
Copy Markdown
Contributor Author

Anything to help make the environment and framework better @cpojer 🤝

@mdvacca - let me know if you need any help at all.

and finally, is there any plans to update documentation/examples/blog posts etc? Would be happy to help on that front as well.

As soon as Hudl makes use of the merged code in a future release, we'll write up our own blog post to sunset https://github.com/hudl/react-native-android-fragment as well, with a migration plan in place.

@patrickkempff

Copy link
Copy Markdown
Contributor

@mdvacca any progress on the fix? Would love to help if needed.

@mdvacca

mdvacca commented May 28, 2019

Copy link
Copy Markdown
Contributor

@patrickkempff this requires some changes internally at FB, I didn't have the time to take a look into it yet but I'm planning to work on it the next few weeks I will let you know If I need an extra hand!
Thanks

@mdvacca

mdvacca commented Jun 10, 2019

Copy link
Copy Markdown
Contributor

Today we fixed and landed this PR again.

@patrickkempff

Copy link
Copy Markdown
Contributor

@mdvacca great news, awesome!

@saltyskip

saltyskip commented Jul 24, 2019 •

Copy link
Copy Markdown

Any tutorial/guide on how to use this functionality?

I'd like to basically follow what is listed here

https://facebook.github.io/react-native/docs/integration-with-existing-apps

for Android but replace the activity with fragments as my application is based entirely around fragments.

I cannot tell if this is already included in a release version of React Native. I am using 0.60.4, but the ReactFragment class cannot be found

EDIT: This feature seems to not be present in 0.60.4 is there any timeline for when then this will be included in a release @mdvacca

@jpshelley

Copy link
Copy Markdown
Contributor Author

@saltyskip until its released you can also leverage this library - https://github.com/hudl/react-native-android-fragment

this was the original work that got merged into master here. The API/Implementation should remain the same, so you can check out the example app.

Once Hudl is able to use the React-Native version, I'll follow-up and add documentation to React Native and an example, if someone else doesn't beat me to it.

@saltyskip

Copy link
Copy Markdown

Thanks @jpshelley got it to work with your original library. Great work, and looking forward to when this functionality is included in an official release :)

@devvit

devvit commented Oct 5, 2019 •

Copy link
Copy Markdown

@mdvacca ReactDelegate should be exposed, consider the scene react-navigation

public class MainActivity extends ReactActivity {

  @Override
  protected String getMainComponentName() {
    return "Example";
  }

+  @Override
+  protected ReactActivityDelegate createReactActivityDelegate() {
+    return new ReactActivityDelegate(this, getMainComponentName()) {
+      @Override
+      protected ReactRootView createRootView() {
+       return new RNGestureHandlerEnabledRootView(MainActivity.this);
+      }
+    };
+  }
}

Currently(RN 0.61), there isnt a interface to do that

@IanVS

IanVS commented Jan 22, 2020

Copy link
Copy Markdown

I found my way to this PR after trying to add react-navigation to an old brownfield app that's slowly been migrated over the years up to RN 0.59. It seems to use the strategy in https://facebook.github.io/react-native/docs/integration-with-existing-apps, but those docs say nothing about ReactActivity or MainActivity, so I'm not sure what I need to do to get react-native-gesture-handler working as @devvit shows above. Are the docs still correct, in that I should be extending my activity from Activity? Or should I now extend from ReactActivity which I guess was added by this PR? Sorry to spam this old PR, but I'm not a native android developer and I've been struggling with this most of the day. My main question is whether https://facebook.github.io/react-native/docs/integration-with-existing-apps is up-to-date, or if there are any better resources for how to integrate RN into an existing android app. 🙏 🙇

@dulmandakh

Copy link
Copy Markdown
Contributor

If you want to use React, then ReactActivity is the way to go.

@IanVS

IanVS commented Jan 22, 2020

Copy link
Copy Markdown

That's great to hear, thanks. Is there any documentation about it, since apparently the official docs are out-of-date?

@BharathMG-ShareChat

Copy link
Copy Markdown

@mdvacca ReactDelegate should be exposed, consider the scene react-navigation

public class MainActivity extends ReactActivity {

  @Override
  protected String getMainComponentName() {
    return "Example";
  }

+  @Override
+  protected ReactActivityDelegate createReactActivityDelegate() {
+    return new ReactActivityDelegate(this, getMainComponentName()) {
+      @Override
+      protected ReactRootView createRootView() {
+       return new RNGestureHandlerEnabledRootView(MainActivity.this);
+      }
+    };
+  }
}

Currently(RN 0.61), there isnt a interface to do that

How to solve this issue? Currently, only way is to rewrite ReactFragment and wrap ReactRootView with Gesture handler?

@dimgarof

dimgarof commented Jun 19, 2020 •

Copy link
Copy Markdown

Hello,
so I 'm trying to use com.facebook.react.ReactFragment by extending it.

Unfortunately, that's not possible

private static ReactFragment newInstance(String componentName, Bundle launchOptions)

This should be

public static Bundle getReactFragmentBundle(String componentName, Bundle launchOptions)

This way I can get a bundle that's required by your ReactFragment, and add my own args in it.
The only way I can use your solution is by copypasting the entire ReactFragment code.

Any plans on fixing this?

@keeslinp

Copy link
Copy Markdown

For any future folks with @mitsest's question. You can use the Builder to do something like ReactFragment.Builder().setComponentName(name).setLaunchArguments(launchOptionsBundle) instead of using newInstance directly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. Platform: Android Android applications. Ran Commands One of our bots successfully processed a command.

Projects

None yet

Development

Successfully merging this pull request may close these issues.