Skip to content

Commit

Permalink
chore: rename repo (#18)
Browse files Browse the repository at this point in the history
* chore: rename project name in contribution and readme

* chore: rename android example project name

* chore: rename ios exmaple app name

* chore: rename in readme

* chore: rename in library

* chore: fix error message

* chore: update working illustration

* chore: add platform check for localhost url in example app
  • Loading branch information
kunalchavhan authored Jun 20, 2024
1 parent 7eb3ffc commit ada9f80
Show file tree
Hide file tree
Showing 56 changed files with 1,886 additions and 331 deletions.
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ The [example app](/example/) demonstrates usage of the library. You need to run

It is configured to use the local version of the library, so any changes you make to the library's source code will be reflected in the example app. Changes to the library's JavaScript code will be reflected in the example app without a rebuild, but native code changes will require a rebuild of the example app.

If you want to use Android Studio or XCode to edit the native code, you can open the `example/android` or `example/ios` directories respectively in those editors. To edit the Objective-C or Swift files, open `example/ios/ServerComponentExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > react-native-server-component`.
If you want to use Android Studio or XCode to edit the native code, you can open the `example/android` or `example/ios` directories respectively in those editors. To edit the Objective-C or Swift files, open `example/ios/RemoteComponentExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > react-native-remote-component`.

To edit the Java or Kotlin files, open `example/android` in Android studio and find the source files at `react-native-server-component` under `Android`.
To edit the Java or Kotlin files, open `example/android` in Android studio and find the source files at `react-native-remote-component` under `Android`.

You can use various commands from the root directory to work with the project.

Expand Down
64 changes: 32 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
# react-native-server-component
# react-native-remote-component

Server Component allow react-native (Host) applications to render remote (Server) components. Remote components are loaded through URI at runtime. Remotely loaded components behaves similar to the locally imported components.
Remote Component allow react-native (Host) applications to render remote (Server) components. Remote components are loaded through URI at runtime. Remotely loaded components behaves similar to the locally imported components.

Server Component are babel transpiled source code of tsx or jsx, which is executed at runtime. This gives capability to update/change UI without app release. Server Components can use hooks like `useState` and also react lifecycle events like `useEffect`.
Remote Component are babel transpiled source code of tsx or jsx, which is executed at runtime. This gives capability to update/change UI without app release. Remote Components can use hooks like `useState` and also react lifecycle events like `useEffect`.

## Installation

```sh
npm install react-native-server-component
npm install react-native-remote-component
```

## Usage

### Server Component
### Remote Component

```tsx
// Host Application Component using ServerComponent
// Host Application Component using RemoteComponent

import * as React from 'react';
import { View } from 'react-native';
import { ServerComponent } from 'react-native-server-component';
import { RemoteComponent } from 'react-native-remote-component';

const FallbackComponent = () => {
return (
Expand All @@ -32,7 +32,7 @@ const FallbackComponent = () => {
export default function App() {
return (
<View style={{ flex: 1 }}>
<ServerComponent
<RemoteComponent
source={{ uri: 'https://api.server.com/promotion-card.jsx' }}
fallbackComponent={<FallbackComponent />}
/>
Expand All @@ -42,12 +42,12 @@ export default function App() {
```

```tsx
// Server Component hosted on server
// Remote Component hosted on server

export const HomeComponent = () => {
return (
<View>
<Text> Server Component </Text>
<Text> Remote Component </Text>
</View>
);
};
Expand All @@ -59,17 +59,17 @@ export const HomeComponent = () => {
import * as React from 'react';
import { View } from 'react-native';
import {
ServerComponent,
preloadServerComponent,
} from 'react-native-server-component';
RemoteComponent,
preloadRemoteComponent,
} from 'react-native-remote-component';

export default function App() {
// make sure to preload before actual usage
// components are cached againt URI

const preloadComponent = async () => {
try {
const { preload } = preloadServerComponent({});
const { preload } = preloadRemoteComponent({});
await preload('https://api.server.com/player-card.jsx');
} catch (e) {
console.error('Failed to preload. ', e);
Expand All @@ -82,7 +82,7 @@ export default function App() {

return (
<View style={{ flex: 1 }}>
<ServerComponent
<RemoteComponent
source={{ uri: 'https://api.server.com/player-card.jsx' }}
fallbackComponent={<FallbackComponent />}
/>
Expand All @@ -95,25 +95,25 @@ export default function App() {

![Alt text](./docs/working.png)

Server Component requires transpiled \*.tsx (jsx) code to be executed at runtime in host application. Babel is used to transpile the .tsx or .jsx file in format Server Component can understand.
Remote Component requires transpiled \*.tsx (jsx) code to be executed at runtime in host application. Babel is used to transpile the .tsx or .jsx file in format Remote Component can understand.

Babel command to transpile tsx or jsx

```sh
npx babel --presets=@babel/preset-env,@babel/preset-react ExampleServerComponent.tsx -o TranspiledExample.js
npx babel --presets=@babel/preset-env,@babel/preset-react ExampleRemoteComponent.tsx -o TranspiledExample.js
```

Transpiled source code must be served from URL to Server Component. Since server component executes transpiled source code at runtime, right now only vanilla react native components can be used in Server Component. For any third party library usage, import must be resolved at runtime. Resolving imports for third party dependencies can be done by providing `global` prop. For successful import resolution at runtime, the third party dependency must be part of original bundle shipped with host application.
Transpiled source code must be served from URL to Remote Component. Since remote component executes transpiled source code at runtime, right now only vanilla react native components can be used in Remote Component. For any third party library usage, import must be resolved at runtime. Resolving imports for third party dependencies can be done by providing `global` prop. For successful import resolution at runtime, the third party dependency must be part of original bundle shipped with host application.

```tsx
// Server Component hosted on server
// Remote Component hosted on server

import * as React from 'react';
// Buttton component used from react-native-elements
import { Button } from '@rneui/base';
import { View } from 'react-native';

const ServerComponent = () => {
const RemoteComponent = () => {
return (
<View>
<Button title="Hello World!" />;
Expand All @@ -122,15 +122,15 @@ const ServerComponent = () => {
};
```

To resolve import of `Button` at runtime in host application, `global` prop must be provided to Server Component
To resolve import of `Button` at runtime in host application, `global` prop must be provided to Remote Component

```tsx
// Host application component using server component
// Host application component using remote component

const App = () => {
return (
<View>
<ServerComponent
<RemoteComponent
global={{
require: (moduleId: string) => {
if (moduleId === '@rneui/base') {
Expand All @@ -152,16 +152,16 @@ const App = () => {
- `fallbackComponent`
- Fallback component provided to React Suspense
- `errorComponent`
- Component to be used in case of error in ServerComponent
- Component to be used in case of error in RemoteComponent
- `loadingComponent`
- `onAction`
- Callback with `action` and `payload`. Current supported actions are `NAVIGATE`, `IO`.
- `global`
- Custom import resolution, used by Server Component at runtime
- Custom import resolution, used by Remote Component at runtime

## Handling Actions on Server Component
## Handling Actions on Remote Component

Server Component is capable of handling all the user interactions. They can emit event to let host application know about actions, host application needs to implement `onAction` callback provided by Server Component. `onAction` callback has two parameters action type and payload
Remote Component is capable of handling all the user interactions. They can emit event to let host application know about actions, host application needs to implement `onAction` callback provided by Remote Component. `onAction` callback has two parameters action type and payload

```tsx
// Host application
Expand All @@ -177,7 +177,7 @@ const handleAction = useCallback(
[navigation]
);

<ServerComponent
<RemoteComponent
source={{ uri: 'https://api.server.com/card.jsx' }}
fallbackComponent={<FallbackComponent />}
onAction={handleAction}
Expand All @@ -187,9 +187,9 @@ const handleAction = useCallback(
Action emitted contains action type and payload.

```tsx
// Example Server Component
// Example Remote Component

const ExampleServerComponent = ({
const ExampleRemoteComponent = ({
onAction,
}: {
onAction: (action: any, payload: Record<string, any>) => void;
Expand All @@ -214,11 +214,11 @@ const ExampleServerComponent = ({

## Component Caching

Server Components are cached in-memory for URI. Internally axios is used to fetch source from URI. `Cache-Control` header in response is used to burst cache session. `Cache-Control` should follow standard format e.g. `max-age=$value` where `value` is in milliseconds.
Remote Components are cached in-memory for URI. Internally axios is used to fetch source from URI. `Cache-Control` header in response is used to burst cache session. `Cache-Control` should follow standard format e.g. `max-age=$value` where `value` is in milliseconds.

## Running example app

Example has `server` folder which contains express server and mocks for Server Component.
Example has `server` folder which contains express server and mocks for Remote Component.

```sh
cd example
Expand Down
8 changes: 4 additions & 4 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
buildscript {
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["ServerComponent_kotlinVersion"]
def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["RemoteComponent_kotlinVersion"]

repositories {
google()
Expand All @@ -26,11 +26,11 @@ if (isNewArchitectureEnabled()) {
}

def getExtOrDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["ServerComponent_" + name]
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["RemoteComponent_" + name]
}

def getExtOrIntegerDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["ServerComponent_" + name]).toInteger()
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["RemoteComponent_" + name]).toInteger()
}

def supportsNamespace() {
Expand All @@ -44,7 +44,7 @@ def supportsNamespace() {

android {
if (supportsNamespace()) {
namespace "com.servercomponent"
namespace "com.RemoteComponent"

sourceSets {
main {
Expand Down
10 changes: 5 additions & 5 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ServerComponent_kotlinVersion=1.7.0
ServerComponent_minSdkVersion=21
ServerComponent_targetSdkVersion=31
ServerComponent_compileSdkVersion=31
ServerComponent_ndkversion=21.4.7075529
RemoteComponent_kotlinVersion=1.7.0
RemoteComponent_minSdkVersion=21
RemoteComponent_targetSdkVersion=31
RemoteComponent_compileSdkVersion=31
RemoteComponent_ndkversion=21.4.7075529
2 changes: 1 addition & 1 deletion android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.servercomponent">
package="com.remotecomponent">
</manifest>
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.servercomponent
package com.remotecomponent

import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.Promise

class ServerComponentModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
class RemoteComponentModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {

override fun getName(): String {
return NAME
Expand All @@ -20,6 +20,6 @@ class ServerComponentModule(reactContext: ReactApplicationContext) :
}

companion object {
const val NAME = "ServerComponent"
const val NAME = "RemoteComponent"
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package com.servercomponent
package com.remotecomponent

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager


class ServerComponentPackage : ReactPackage {
class RemoteComponentPackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return listOf(ServerComponentModule(reactContext))
return listOf(RemoteComponentModule(reactContext))
}

override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
Expand Down
Binary file modified docs/working.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified example/.yarn/install-state.gz
Binary file not shown.
Loading

0 comments on commit ada9f80

Please sign in to comment.