Skip to content

Commit

Permalink
chore: example react-navigation (gronxb#30)
Browse files Browse the repository at this point in the history
* chore: example react-navigation

* docs: example guide
  • Loading branch information
gronxb authored Feb 15, 2024
1 parent 7e301d7 commit 13ddfb8
Show file tree
Hide file tree
Showing 74 changed files with 4,004 additions and 2 deletions.
10 changes: 10 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ export default defineConfig({
},
],
},
{
text: "Example Guide",
base: "/example",
items: [
{
text: "React Navigation",
link: "/react-navigation",
},
],
},
{
text: "API Reference",
items: [
Expand Down
226 changes: 226 additions & 0 deletions docs/example/react-navigation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
# Example React Navigation

This guide covers how to declare state in React Native and share it with the web.

<video width="320" height="240" muted autoplay loop>
<source src="/react-navigation.mp4" type="video/mp4">
</video>

Example: [react-navigation](https://github.com/gronxb/webview-bridge/tree/main/example/react-navigation)


## React Native Part

```tsx
// This file is App.tsx
import React from "react";

import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import type { NativeStackScreenProps } from "@react-navigation/native-stack";
import { View, Text, Button } from "react-native";
import { WebView, navigationRef } from "./src/bridge";
import { RootStackParamList } from "./src/navigation";

function WebViewHomeScreen() {
return (
<View style={{ height: "100%" }}>
<WebView
source={{
uri: "http://localhost:5173",
}}
style={{ height: "100%", flex: 1, width: "100%" }}
/>
</View>
);
}

function UserInfoScreen({
navigation,
route,
}: NativeStackScreenProps<RootStackParamList, "UserInfo">) {
const { userId } = route.params;

return (
<View style={{ height: "100%" }}>
<Text>UserId: {userId}</Text>

<Button
title="New WebView"
onPress={() => navigation.push("WebViewHome")}
/>
<Button title="Go back" onPress={() => navigation.goBack()} />
</View>
);
}

const Stack = createNativeStackNavigator<RootStackParamList>();

function App(): JSX.Element {
return (
<NavigationContainer ref={navigationRef}>
<Stack.Navigator initialRouteName="WebViewHome">
<Stack.Screen name="WebViewHome" component={WebViewHomeScreen} />
<Stack.Screen name="UserInfo" component={UserInfoScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}

export default App;

```

```tsx
// This file is src/bridge.ts

import {
StackActions,
createNavigationContainerRef,
} from "@react-navigation/native";
import { createWebView, bridge } from "@webview-bridge/react-native";
import InAppBrowser from "react-native-inappbrowser-reborn";
import { RootStackParamList } from "./navigation";

export const navigationRef = createNavigationContainerRef<RootStackParamList>();

export const appBridge = bridge({
async getMessage() {
return "I'm from native" as const;
},
async openInAppBrowser(url: string) {
if (await InAppBrowser.isAvailable()) {
await InAppBrowser.open(url);
}
},
async canGoBack() {
return Boolean(
navigationRef.current?.isReady() && navigationRef.current.canGoBack(),
);
},
async goBack() {
if (navigationRef.current?.isReady()) {
navigationRef.current.goBack();
}
},
async navigate<RouteName extends keyof RootStackParamList>(
name: RouteName,
params: RootStackParamList[RouteName],
) {
if (navigationRef.current?.isReady()) {
navigationRef.current.navigate(name as any, params as any);
}
},
async push<RouteName extends keyof RootStackParamList>(
name: RouteName,
params: RootStackParamList[RouteName],
) {
if (navigationRef.current?.isReady()) {
navigationRef.current.dispatch(StackActions.push(name, params));
}
},
async replace<RouteName extends keyof RootStackParamList>(
name: RouteName,
params: RootStackParamList[RouteName],
) {
if (navigationRef.current?.isReady()) {
navigationRef.current.dispatch(StackActions.replace(name, params));
}
},
async popToTop() {
if (navigationRef.current?.isReady()) {
navigationRef.current.dispatch(StackActions.popToTop());
}
},
});

// It is exported via the package.json type field.
export type AppBridge = typeof appBridge;

export const { WebView, linkWebMethod } = createWebView({
bridge: appBridge,
debug: true,
fallback: (method) => {
console.warn(`Method '${method}' not found in native`);
},
});

```



## Web (React) Part
```tsx
// This file is App.tsx
import { useState } from "react";
import { linkBridge } from "@webview-bridge/web";
import type { AppBridge } from "@webview-bridge-example-react-navigation/react-native";

const bridge = linkBridge<AppBridge>({
throwOnError: true,
onReady: () => {
console.log("bridge is ready");
},
});

function App() {
const [userId, setUserId] = useState("");

return (
<div>
<h3>This is a web page.</h3>

<div
style={{
display: "flex",
flexDirection: "column",
gap: "12px",
}}
>
<button
onClick={() => {
if (bridge.isNativeMethodAvailable("openInAppBrowser") === true) {
bridge.openInAppBrowser(
"https://github.com/gronxb/webview-bridge",
);
}
}}
>
open InAppBrowser
</button>

<input
type="text"
style={{
fontSize: "16px",
}}
value={userId}
onChange={(e) => setUserId(e.target.value)}
placeholder="please userId"
/>
<button
onClick={() => {
bridge.push("UserInfo", { userId });
}}
>
Go UserInfo
</button>

<button
onClick={async () => {
if (await bridge.canGoBack()) {
bridge.goBack();
} else {
alert("Can't go back");
}
}}
>
Go Back
</button>
</div>
</div>
);
}

export default App;
```
Binary file added docs/public/react-navigation.mp4
Binary file not shown.
2 changes: 2 additions & 0 deletions example/react-navigation/react-native/.bundle/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
BUNDLE_PATH: "vendor/bundle"
BUNDLE_FORCE_RUBY_PLATFORM: 1
4 changes: 4 additions & 0 deletions example/react-navigation/react-native/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: '@react-native',
};
139 changes: 139 additions & 0 deletions example/react-navigation/react-native/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Xcode
!**/*.xcodeproj
!**/*.pbxproj
!**/*.xcworkspacedata
!**/*.xcsettings
!**/*.xcscheme
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
**/.xcode.env.local

# Gradle
/build/
/packages/react-native-gradle-plugin/build/
/packages/rn-tester/build
/packages/rn-tester/android/app/.cxx/
/packages/rn-tester/android/app/build/
/packages/rn-tester/android/app/gradle/
/packages/rn-tester/android/app/gradlew
/packages/rn-tester/android/app/gradlew.bat
/ReactAndroid/build/
/ReactAndroid/.cxx/
/ReactAndroid/gradle/
/ReactAndroid/gradlew
/ReactAndroid/gradlew.bat
/ReactAndroid/external-artifacts/build/
/ReactAndroid/external-artifacts/artifacts/
/ReactAndroid/hermes-engine/build/
/ReactAndroid/hermes-engine/.cxx/
/template/android/app/build/
/template/android/build/

# Buck
.buckd
buck-out
/.lsp.buckd
/.lsp-buck-out
/ReactAndroid/src/main/jni/prebuilt/lib/
/ReactAndroid/src/main/gen

# Android Studio
.project
.settings
.classpath

# Watchman
.watchmanconfig

# Android
.idea
.gradle
local.properties
*.iml
!/android/README.md
key.properties

# Node
node_modules
*.log
.nvm
package-lock.json

# OS X
.DS_Store

# Test generated files
/ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
*.js.meta

/coverage
/third-party

# Test Reports
/reports

# Stack Dumps generated when programs crash (Ex. bash.exe.stackdump on Win)
*.stackdump

# Root dir shouldn't have Xcode project
/*.xcodeproj

# ReactCommon subdir shouldn't have Xcode project
/ReactCommon/**/*.xcodeproj

# Libs that shouldn't have Xcode project
/Libraries/FBLazyVector/**/*.xcodeproj
/Libraries/RCTRequired/**/*.xcodeproj
/React/CoreModules/**/*.xcodeproj
/React/FBReactNativeSpec/**/*.xcodeproj
/packages/react-native-codegen/**/*.xcodeproj

# Ruby Gems (Bundler)
/vendor
/template/vendor

# iOS / CocoaPods
Pods
build
/template/ios/build/
/template/ios/Pods/
/template/ios/Podfile.lock
/packages/rn-tester/Gemfile.lock

# Ignore RNTester specific Pods, but keep the __offline_mirrors__ here.
/packages/rn-tester/Pods/*
!/packages/rn-tester/Pods/__offline_mirrors_hermes__
!/packages/rn-tester/Pods/__offline_mirrors_jsc__

# @react-native/codegen
/React/FBReactNativeSpec/FBReactNativeSpec
/packages/react-native-codegen/lib
/packages/react-native-codegen/tmp/
/ReactCommon/react/renderer/components/rncore/
/packages/rn-tester/react-nativeModuleExample/ScreenshotManagerSpec*


# Additional SDKs
/sdks/download
/sdks/hermes
/sdks/hermesc

# Android memory profiler files
*.hprof

# Temporary files created by Metro to check the health of the file watcher
.metro-health-check*
Loading

0 comments on commit 13ddfb8

Please sign in to comment.