{"id":722,"date":"2020-09-08T14:00:07","date_gmt":"2020-09-08T21:00:07","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/surface-duo\/?p=722"},"modified":"2020-09-24T10:18:37","modified_gmt":"2020-09-24T17:18:37","slug":"new-react-native-dual-screen-navigation-module","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/surface-duo\/new-react-native-dual-screen-navigation-module\/","title":{"rendered":"New React Native dual-screen navigation module"},"content":{"rendered":"<p>\n  Hello, React Native for Android developers!\n<\/p>\n<p>\n  In a <a href=\"https:\/\/devblogs.microsoft.com\/surface-duo\/build-react-native-apps-for-microsoft-surface-duo\/?WT.mc_id=blog-surfaceduoblog-gt~downunder\">previous blog post<\/a>, we discussed these existing React Native dual-screen features: <a href=\"https:\/\/www.npmjs.com\/package\/react-native-dualscreeninfo\">DualScreenInfo<\/a> to find information about the device, and <a href=\"https:\/\/www.npmjs.com\/package\/react-native-twopaneview\">TwoPaneView<\/a> to get assistance with building dual-screen apps.  Today\u2019s post is about a new module we\u2019ve released for JavaScript and TypeScript that makes it easier to manage component states and move apps around on dual-screen devices. \n<\/p>\n<p>\n  The <a href=\"https:\/\/github.com\/microsoft\/react-native-dualscreen\/tree\/master\/twopane-navigation\">TwoPane-Navigation<\/a> module was built to solve the problem of retaining state across screens when apps shift positions on dual-screen devices and evolved into a powerful library that empowers developers to build apps with less focus on navigation problems. It is built to work side by side with the react-navigation library or work on its own as a replacement. This allows you, the developer, to have a flexible and premium developer experience to help pioneer the new wave of dual-screen device applications.\n<\/p>\n<h2>About TwoPane-Navigation<\/h2>\n<p>\n  The TwoPane-Navigation library leverages <a href=\"https:\/\/react-redux.js.org\/\">Redux<\/a> and is built around the core concept of viewing each pane (screen) on your dual-screen device as its own stack (LIFO), rendering the top element of the stack as the current page.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"796\" height=\"512\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/stack-example.png\" class=\"wp-image-723\" alt=\"stack Example\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/stack-example.png 796w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/stack-example-300x193.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/stack-example-768x494.png 768w\" sizes=\"(max-width: 796px) 100vw, 796px\" \/>\n<\/p>\n<p><em>Figure 1: Two conceptual navigation stacks, one on each screen<\/em>\n<\/p>\n<p>\n  The new <a href=\"https:\/\/github.com\/microsoft\/react-native-dualscreen\/tree\/master\/twopane-navigation\/src\/components\/twoPaneApp\">TwoPaneApp<\/a> must be the base for your React Native application and has the following properties:\n<\/p>\n<ul>\n<li><strong>onePaneDefault<\/strong> &#8211; this will be the base pane you want when your application is in onePane View\n  <\/li>\n<li><strong>twoPaneDefault<\/strong> &#8211; this will be the base pane you want when your application is in twoPane View\n  <\/li>\n<li><strong>config?<\/strong> &#8211; overrides default values for your application\n  <\/li>\n<li><strong>navigationContainer?<\/strong> &#8211; AppContainer if using React Navigation\n  <\/li>\n<\/ul>\n<p>\n  The code below shows an example application definition:\n<\/p>\n<pre>const App = () => {\r\n  return (\r\n    &lt;TwoPaneApp \r\n      onePaneDefault={ TwoPaneAppDefaultComponents.onePaneDefault }\r\n      twoPaneDefault={ TwoPaneAppDefaultComponents.twoPaneDefault }\r\n      config={ TwoPaneAppDefaultComponents.config }\r\n    \/>\r\n  );\r\n};\r\n<\/pre>\n<p>\n  The components are defined below \u2013 properties can be set or overridden for each pane:\n<\/p>\n<pre>const TwoPaneAppDefaultComponents: ITwoPaneAppProps = {\r\n  onePaneDefault: {\r\n    key: 'onePane',\r\n    paneElement: &lt;OnePane \/>,\r\n    header: {\r\n      title: 'OnePane Title'\r\n    }\r\n\r\n  },\r\n  twoPaneDefault: {\r\n    key: 'twoPane',\r\n    paneElement: &lt;TwoPane \/>,\r\n    header: { \r\n      title: 'TwoPane Title'\r\n    }\r\n  },\r\n  config: {\r\n    onePane: {\r\n      paneHeader: {\r\n        backgroundColor: 'gray'\r\n      },\r\n      paneBody: {\r\n        backgroundColor: 'black'\r\n      }\r\n    },\r\n    twoPane: {\r\n      paneHeader: {\r\n        backgroundColor: 'gray'\r\n      },\r\n      paneBody: {\r\n        backgroundColor: 'black'\r\n      }\r\n      paneHeaderText: {\r\n        color: 'red'\r\n      },\r\n      paneHeaderIcon: {\r\n        tintColor: 'red'\r\n      }\r\n    }\r\n  }\r\n}\r\n<\/pre>\n<p>\n  The TwoPaneApp can automatically detect and handle the display on one screen or being spanned across two screens. When the app is first opened, we will see:\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"533\" height=\"687\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/onepane-example.png\" class=\"wp-image-724\" alt=\"onePane Example\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/onepane-example.png 533w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/onepane-example-233x300.png 233w\" sizes=\"(max-width: 533px) 100vw, 533px\" \/>\n  <br \/><em>Figure 2: Only the first pane is visible on startup<\/em>\n<\/p>\n<p>\n  If we span the application across both screens, we will see:\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"1114\" height=\"679\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/twopane-example.png\" class=\"wp-image-725\" alt=\"twoPane Example\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/twopane-example.png 1114w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/twopane-example-300x183.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/twopane-example-1024x624.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/twopane-example-768x468.png 768w\" sizes=\"(max-width: 1114px) 100vw, 1114px\" \/>\n  <br \/><em>Figure 3: Both panes are visible when the app is spanned<\/em>\n<\/p>\n<ul>\n<li>\n    Notice in <strong>twoPane<\/strong> mode, our header text is red. This is because we provided an override on our default text color in our config during setup above.\n  <\/li>\n<li>\n    Notice how the <strong>onePane<\/strong> mode is now showing in the first panel as its own separate entity.\n  <\/li>\n<\/ul>\n<h2>Panes<\/h2>\n<ul>\n<li><strong>onePane<\/strong>&#8211; will call every action specifically for the onePane stack\n  <\/li>\n<li><strong>twoPane<\/strong> &#8211; will call every action specifically for the twoPane stack\n  <\/li>\n<li><strong>autoPane<\/strong> &#8211; will check to see if there is onePane or twoPane currently active, and will call the appropriate action to the current pane\n  <\/li>\n<\/ul>\n<p>\n  The Panes have a variety of Hooks and Methods, which are outlined in the <a href=\"https:\/\/www.npmjs.com\/package\/react-native-twopane-navigation&quot; \\l &quot;panes\">TwoPane-Navigation docs<\/a>.\n<\/p>\n<h2>Navigating Panes<\/h2>\n<p>\n  Now with our TwoPane-Navigation app set up, we can finally start navigating.\n<\/p>\n<p>\n  To navigate between panes, we have a variety of built-in methods, but for this example, we\u2019ll only introduce you to the core methods.\n<\/p>\n<h3>Moving Forward<\/h3>\n<p>\n  The syntax for moving forward has a number of options for maximum flexibility. Calling Pane.Add pushes an element to the top of the stack, or replaces the original with the new element.\n<\/p>\n<p><code>(one | two | auto)Pane.Add(key: string, element: ReactElement, header?: IHeader, isMergeONE = false, isMergeTWO = false)<\/code>\n<\/p>\n<p>\n  For this example, we will be pushing to twoPane if both panes are active, or we will be pushing to the onePane if only using one pane.\n<\/p>\n<pre>autoPane.Add(\r\n   'exampleScreen',\r\n   &lt;NextExampleScreen \/>,\r\n   header: {\r\n     title: 'NextExampleScreen'\r\n   },\r\n   true,\r\n   true)\r\n<\/pre>\n<p>\n  If we run our application in twoPane mode, we will see that we have only pushed the new screen to the twoPane stack (indicated by the back button on the second screen):\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"1197\" height=\"730\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/twopaneview-example.png\" class=\"wp-image-726\" alt=\"twoPaneView Example\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/twopaneview-example.png 1197w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/twopaneview-example-300x183.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/twopaneview-example-1024x624.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/twopaneview-example-768x468.png 768w\" sizes=\"(max-width: 1197px) 100vw, 1197px\" \/>\n  <br \/><em>Figure 4: the second screen has a navigation stack<\/em>\n<\/p>\n<p>\n  Because we provided an override on our default in twoPane config, the text color for the header is red.\n<\/p>\n<h3>Going Back<\/h3>\n<p>\n  The header provided by TwoPane-Navigation automatically includes a back button when it\u2019s possible to go back from the current pane. Note: If there\u2019s only one pane in the stack, there\u2019s nothing to go back to, so there\u2019s no back button.\n<\/p>\n<p>\n  If you want to programmatically go back one element in the navigation stack, you can do so by calling:\n<\/p>\n<p><code>(one | two | auto)Pane.GoBack()<\/code> \n<\/p>\n<pre>&lt;TouchableOpacity\r\n  onPress={ () => onePane.GoBack() }>\r\n  &lt;Text>Press me to go back in our onePane stack&lt;\/Text>\r\n&lt;\/TouchableOpacity><\/pre>\n<p>\n  If you have multiple panes in the stack and would like to go back to the very first pane in your stack (defaultPane), you can use:\n<\/p>\n<p><code>(one | two | auto)Pane.BackToHome()<\/code> \n<\/p>\n<p>\n  BackToHome removes all elements of the stack and returns the base component.\n<\/p>\n<pre>&lt;TouchableOpacity\r\n  onPress={ () => onePane.BackToHome() }>\r\n  &lt;Text>Press me to go back to our default pane in our onePane stack&lt;\/Text>\r\n&lt;\/TouchableOpacity><\/pre>\n<h3>React Navigation<\/h3>\n<p>\n  To use React Navigation with the TwoPane-Navigation module, create the required navigators (i.e. StackNavigators, SwitchNavigators, DrawerNavigators, etc.) and pass the appContainer into the navigationContainer prop in our TwoPaneApp component. After you have added the required navigators, you can then access the navigation\/route objects with the useNavigation\/useRoute hooks.\n<\/p>\n<pre>const App = () => {\r\n  return (\r\n    &lt;TwoPaneApp \r\n      onePaneDefault={ TwoPaneAppDefaultComponents.onePaneDefault }\r\n      twoPaneDefault={ TwoPaneAppDefaultComponents.twoPaneDefault }\r\n      config={ TwoPaneAppDefaultComponents.config }\r\n      navigationContainer={ AppContainer() }\r\n    \/>\r\n  );\r\n};\r\nconst AppContainer = () => {\r\n  return (\r\n      &lt;NavigationContainer>\r\n        <Main.Screen\r\n          name=\"base\"\r\n          component={TwoPaneHub}\r\n          options={{ headerShown: false }}\r\n        \/>\r\n      &lt;\/NavigationContainer>\r\n  );\r\n};<\/pre>\n<h3>Pane Merging<\/h3>\n<p>\n  When you want to keep panes through pane transition from onePane to twoPane mode, you need to mark the screens you want as isMergeOne or isMergeTwo.\n<\/p>\n<p>\n  It is very simple to enable this behavior. Simply mark <code>isMerge = true<\/code> when you add your screen to the stack.\n<\/p>\n<pre>onePane.Add(\r\n  'NextExampleScreen',\r\n  &lt;NextExampleScreen \/>,\r\n  header: {\r\n    title: 'NextExampleScreen'\r\n  },\r\n  true\r\n);\r\n<\/pre>\n<p>\n  With this setting, when the application is unspanned to a single screen, it keeps the correct pane on top, including state:\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"1197\" height=\"730\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/screenmerge-example.png\" class=\"wp-image-727\" alt=\"ScreenMerge Example\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/screenmerge-example.png 1197w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/screenmerge-example-300x183.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/screenmerge-example-1024x624.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/screenmerge-example-768x468.png 768w\" sizes=\"(max-width: 1197px) 100vw, 1197px\" \/>\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"887\" height=\"580\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/screenmerge-example-1.png\" class=\"wp-image-728\" alt=\"ScreenMerge Example\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/screenmerge-example-1.png 887w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/screenmerge-example-1-300x196.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/09\/screenmerge-example-1-768x502.png 768w\" sizes=\"(max-width: 887px) 100vw, 887px\" \/>\n<\/p>\n<p><em> Figure 5: Unspanning while preserving state<\/em>\n<\/p>\n<h2>Resources and feedback <\/h2>\n<p>\n  Visit the <a href=\"https:\/\/docs.microsoft.com\/dual-screen\/react-native\/?WT.mc_id=docs-surfaceduoblog-gt~downunder\">docs<\/a> and <a href=\"https:\/\/github.com\/microsoft\/react-native-dualscreen\/tree\/master\/twopane-navigation\/example\">sample code<\/a> to see more advanced usage, such as pane merging, header customization, and screen overlays, and try the new navigation module in your apps. Please provide us with your feedback on the <a href=\"https:\/\/github.com\/microsoft\/react-native-dualscreen\/tree\/master\/twopane-navigation\/example\">GitHub repo<\/a>.\n<\/p>\n<p>\n  We would also love to hear from you about your overall experience using the SDK, emulator, and what helpers or controls you\u2019d like us to work on next. Please reach out using our <a href=\"http:\/\/aka.ms\/SurfaceDuoSDK-Feedback\">feedback forum<\/a> or message me <a href=\"https:\/\/github.com\/gt-downunder\">GitHub<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello, React Native for Android developers! In a previous blog post, we discussed these existing React Native dual-screen features: DualScreenInfo to find information about the device, and TwoPaneView to get assistance with building dual-screen apps. Today\u2019s post is about a new module we\u2019ve released for JavaScript and TypeScript that makes it easier to manage component [&hellip;]<\/p>\n","protected":false},"author":13129,"featured_media":723,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[411,45],"class_list":["post-722","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-surface-duo-sdk","tag-react-native","tag-surface-duo-sdk"],"acf":[],"blog_post_summary":"<p>Hello, React Native for Android developers! In a previous blog post, we discussed these existing React Native dual-screen features: DualScreenInfo to find information about the device, and TwoPaneView to get assistance with building dual-screen apps. Today\u2019s post is about a new module we\u2019ve released for JavaScript and TypeScript that makes it easier to manage component [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/722","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/users\/13129"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/comments?post=722"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/722\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media\/723"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media?parent=722"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/categories?post=722"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/tags?post=722"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}