Skip to content

Commit

Permalink
refactor(ReactPage): Removing dependency on ReactPage
Browse files Browse the repository at this point in the history
ReactPage is not the right abstraction for managing the lifecycle of React Native. In theory, React Native can run from a background state (with no pages involved), or can be embedded into a UserControl / ContentControl to run multiple React instances simultaneously.

This changeset adds ReactNativeHost to manage the lifecycle of the ReactInstanceManager.
  • Loading branch information
rozele committed Oct 17, 2017
1 parent b97ad28 commit 94fae97
Show file tree
Hide file tree
Showing 16 changed files with 390 additions and 85 deletions.
22 changes: 11 additions & 11 deletions ReactWindows/Playground/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using ReactNative;
using ReactNative;
using ReactNative.Modules.Launch;
using System;
using Windows.ApplicationModel;
Expand All @@ -15,7 +15,7 @@ namespace Playground
/// </summary>
sealed partial class App : Application
{
private readonly ReactPage _reactPage;
private readonly ReactNativeHost _host = new MainReactNativeHost();

/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
Expand All @@ -29,8 +29,6 @@ public App()
this.InitializeComponent();
this.Suspending += OnSuspending;
this.Resuming += OnResuming;

_reactPage = new AppReactPage();
}

/// <summary>
Expand Down Expand Up @@ -74,7 +72,8 @@ protected override void OnActivated(IActivatedEventArgs args)
/// <param name="arguments"></param>
private void OnCreate(string arguments)
{
_reactPage.OnResume(Exit);
_host.OnResume(Exit);
_host.ApplyArguments(arguments);

#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
Expand All @@ -92,8 +91,6 @@ private void OnCreate(string arguments)
// just ensure that the window is active
if (rootFrame == null)
{
_reactPage.OnCreate(arguments);

// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();

Expand All @@ -108,7 +105,10 @@ private void OnCreate(string arguments)
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Content = _reactPage;
rootFrame.Content = new Page
{
Content = _host.OnCreate(),
};
}

// Ensure the current window is active
Expand All @@ -134,7 +134,7 @@ private void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
_reactPage.OnSuspend();
_host.OnSuspend();
}

/// <summary>
Expand All @@ -143,8 +143,8 @@ private void OnSuspending(object sender, SuspendingEventArgs e)
/// <param name="sender">The source of the resume request.</param>
/// <param name="e">Details about the resume request.</param>
private void OnResuming(object sender, object e)
{
_reactPage.OnResume(Exit);
{
_host.OnResume(Exit);
}
}
}
29 changes: 29 additions & 0 deletions ReactWindows/Playground/MainReactNativeHost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using ReactNative;
using ReactNative.Modules.Core;
using ReactNative.Shell;
using System.Collections.Generic;

namespace Playground
{
class MainReactNativeHost : ReactNativeHost
{
public override string MainComponentName => "Playground";

protected override string JavaScriptMainModuleName => "ReactWindows/Playground/index.windows";

#if BUNDLE
protected override string JavaScriptBundleFile => "ms-appx:///ReactAssets/index.windows.bundle";
#endif

protected override List<IReactPackage> Packages => new List<IReactPackage>
{
new MainReactPackage(),
};

#if !BUNDLE || DEBUG
protected override bool UseDeveloperSupport => true;
#else
protected override bool UseDeveloperSupport => false;
#endif
}
}
2 changes: 1 addition & 1 deletion ReactWindows/Playground/Playground.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="AppReactPage.cs" />
<Compile Include="MainReactNativeHost.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions ReactWindows/ReactNative.Net46/ReactNative.Net46.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
<Compile Include="Modules\WebSocket\WebSocketModule.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ReactPage.cs" />
<Compile Include="ReactRootViewExtensions.cs" />
<Compile Include="Shell\MainReactPackage.cs" />
<Compile Include="Touch\IOnInterceptTouchEventListener.cs" />
<Compile Include="Touch\TouchHandler.cs" />
Expand Down
10 changes: 10 additions & 0 deletions ReactWindows/ReactNative.Net46/ReactRootViewExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace ReactNative
{
static class ReactRootViewExtensions
{
public static void OnCreate(this ReactRootView rootView, ReactNativeHost host)
{
// TODO: add global keyboard shortcuts
}
}
}
2 changes: 2 additions & 0 deletions ReactWindows/ReactNative.Shared/ReactNative.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,11 @@
<Compile Include="$(MSBuildThisFileDirectory)Modules\SystemInfo\PlatformConstantsModule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Modules\SystemInfo\ReactNativeVersion.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Pooling\ObjectPool.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ReactNativeHostExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ReactContextInitializedEventArgs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ReactInstanceManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ReactInstanceManagerBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ReactNativeHost.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ReactRootView.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Reflection\EnumHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Reflection\ExpressionExtensions.cs" />
Expand Down
139 changes: 139 additions & 0 deletions ReactWindows/ReactNative.Shared/ReactNativeHost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
using ReactNative.Bridge;
using ReactNative.Common;
using ReactNative.Modules.Core;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ReactNative
{
/// <summary>
/// Simple class that holds the <see cref="ReactInstanceManager"/>.
/// </summary>
public abstract class ReactNativeHost : IAsyncDisposable
{
private ReactInstanceManager _reactInstanceManager;

/// <summary>
/// Get the current <see cref="ReactInstanceManager"/> instance, or create one.
/// </summary>
public ReactInstanceManager ReactInstanceManager
{
get
{
if (_reactInstanceManager == null)
{
_reactInstanceManager = CreateReactInstanceManager();
}

return _reactInstanceManager;
}
}

/// <summary>
/// Checks whether this host contains a Re
/// </summary>
public bool HasInstance
{
get
{
return _reactInstanceManager != null;
}
}

/// <summary>
/// The main component name.
/// </summary>
public abstract string MainComponentName { get; }

/// <summary>
/// Instantiates the JavaScript executor.
/// </summary>
protected virtual Func<IJavaScriptExecutor> JavaScriptExecutorFactory
{
get
{
return null;
}
}

/// <summary>
/// The name of the main module.
/// </summary>
/// <remarks>
/// Determines the URL to fetch the JavaScript bundle from the packager
/// server. It is only used when dev support is enabled.
/// </remarks>
protected virtual string JavaScriptMainModuleName
{
get
{
return "index.windows";
}
}

/// <summary>
/// The custom path of the bundle file.
/// </summary>
/// <remarks>
/// This is used in cases where the bundle should be loaded from a
/// custom path.
/// </remarks>
protected virtual string JavaScriptBundleFile
{
get
{
return null;
}
}

/// <summary>
/// Signals whether developer mode should be enabled.
/// </summary>
protected abstract bool UseDeveloperSupport { get; }

/// <summary>
/// The list of <see cref="IReactPackage"/>s used by the application.
/// </summary>
protected abstract List<IReactPackage> Packages { get; }

/// <summary>
/// Creates a new root view.
/// </summary>
/// <returns>The root view.</returns>
public virtual ReactRootView CreateRootView()
{
return new ReactRootView();
}

/// <summary>
/// Dispose the current instance and release the reference to it.
/// </summary>
/// <returns>
/// A task to await the dispose operation.
/// </returns>
public async Task DisposeAsync()
{
if (_reactInstanceManager != null)
{
await _reactInstanceManager.DisposeAsync();
_reactInstanceManager = null;
}
}

private ReactInstanceManager CreateReactInstanceManager()
{
var builder = new ReactInstanceManagerBuilder
{
UseDeveloperSupport = UseDeveloperSupport,
InitialLifecycleState = LifecycleState.BeforeCreate,
JavaScriptBundleFile = JavaScriptBundleFile,
JavaScriptMainModuleName = JavaScriptMainModuleName,
JavaScriptExecutorFactory = JavaScriptExecutorFactory,
};

builder.Packages.AddRange(Packages);
return builder.Build();
}
}
}
93 changes: 93 additions & 0 deletions ReactWindows/ReactNative.Shared/ReactNativeHostExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using Newtonsoft.Json.Linq;
using System;

namespace ReactNative
{
/// <summary>
/// An application delegate for managing the lifecycle events in React Native.
/// </summary>
public static class ReactNativeHostExtensions
{
/// <summary>
/// Called when the application is first initialized.
/// </summary>
/// <param name="host">The React Native host.</param>
public static ReactRootView OnCreate(this ReactNativeHost host)
{
return OnCreate(host, null);
}

/// <summary>
/// Called when the application is first initialized.
/// </summary>
/// <param name="host">The React Native host.</param>
/// <param name="initialProps">The initial props.</param>
public static ReactRootView OnCreate(this ReactNativeHost host, JObject initialProps)
{
var rootView = host.CreateRootView();
rootView.OnCreate(host);
rootView.StartReactApplication(
host.ReactInstanceManager,
host.MainComponentName,
initialProps);
return rootView;
}

/// <summary>
/// Resumes the React instance manager.
/// </summary>
/// <param name="host">The React Native host.</param>
/// <param name="onBackPressed">
/// The action to take when back is pressed.
/// </param>
public static void OnResume(this ReactNativeHost host, Action onBackPressed)
{
host.ReactInstanceManager.OnResume(onBackPressed);
}

/// <summary>
/// Suspends the React instance manager.
/// </summary>
/// <param name="host">The React Native host.</param>
public static void OnSuspend(this ReactNativeHost host)
{
if (host.HasInstance)
{
host.ReactInstanceManager.OnSuspend();
}
}

/// <summary>
/// Applies the activation arguments.
/// </summary>
/// <param name="host">The React Native host.</param>
/// <param name="arguments">The arguments.</param>
public static void ApplyArguments(this ReactNativeHost host, string arguments)
{
if (!string.IsNullOrEmpty(arguments) && host.HasInstance)
{
var args = arguments.Split(',');

var index = Array.IndexOf(args, "remoteDebugging");
if (index < 0)
{
return;
}

if (args.Length <= index + 1)
{
throw new ArgumentException("Expected value for remoteDebugging argument.", nameof(arguments));
}

bool isRemoteDebuggingEnabled;
if (bool.TryParse(args[index + 1], out isRemoteDebuggingEnabled))
{
if (host.HasInstance)
{
host.ReactInstanceManager.DevSupportManager.IsRemoteDebuggingEnabled = isRemoteDebuggingEnabled;
}
}
}
}
}
}
7 changes: 5 additions & 2 deletions ReactWindows/ReactNative.sln
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio 15
VisualStudioVersion = 15.0.26730.16
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Playground", "Playground\Playground.csproj", "{D52267B5-396F-424A-BB26-C9E750032846}"
EndProject
Expand Down Expand Up @@ -264,4 +264,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7839097F-DB11-48A8-98B2-0570B1F86648}
EndGlobalSection
EndGlobal
Loading

0 comments on commit 94fae97

Please sign in to comment.