Skip to content

Commit

Permalink
chore: update documentation (#7)
Browse files Browse the repository at this point in the history
* chore: add skelton for readme

* chore: move fallback component out of homescreen component

* chore: move server to example

* chore: add relative path for mock in server

* chore: update readme

* chore: update readme

* chore: update readme

* chore: update readme

* chore: update readme

* chore: add image for showing working of server component

* chore: update working image
  • Loading branch information
kunalchavhan authored Apr 18, 2024
1 parent 3a1329d commit 3234ca1
Show file tree
Hide file tree
Showing 10 changed files with 505 additions and 41 deletions.
147 changes: 142 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# react-native-server-component

Server component for react native
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.

## Installation

Expand All @@ -10,14 +10,151 @@ npm install react-native-server-component

## Usage

```js
import { multiply } from 'react-native-server-component';
### Server Component

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

const result = await multiply(3, 7);
import React from 'react';
import { View } from 'react-native';
import { ServerComponent } from 'react-native-server-component';

const FallbackComponent = () => {
return (
<View>
<Text> Fallback Component </Text>
</View>
);
};

export default function App() {
return (
<View style={{ flex: 1 }}>
<ServerComponent
source={{ uri: 'http://10.0.2.2:8080/home-component' }}
fallbackComponent={<FallbackComponent />}
/>
</View>
);
}
```

```tsx
// Server Component hosted on server

export const HomeComponent = () => {
return (
<View>
<Text> Server Component </Text>
</View>
);
};
```

### Pre Load Component

```tsx
import React from 'react';
import { View } from 'react-native';
import { ServerComponent } from 'react-native-server-component';

const { preload } = preloadServerComponent({});
(async () => {
try {
await preload('http://10.0.2.2:8080/detail-component');
} catch (e) {
console.error('Failed to preload. ', e);
}
})();

export default function App() {
return (
<View style={{ flex: 1 }}>
<ServerComponent
source={{ uri: 'http://10.0.2.2:8080/detail-component' }}
fallbackComponent={<FallbackComponent />}
/>
</View>
);
}
```

## How does it work?

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

## Props

- `source`
- URI to fetch component source
- `fallbackComponent`
- `errorComponent`
- Component to be used in case of error in ServerComponent
- `loadingComponent`
- `onAction`
- Callback with `action` and `payload`. Current supported actions are `NAVIGATE`, `IO`.

## Handling Actions on Server 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.

```tsx
// Host application

const handleAction = useCallback(
(action: string, payload: Record<string, any>) => {
switch (action) {
case 'NAVIGATE':
navigation.navigate(payload.route);
break;
}
},
[navigation]
);

<ServerComponent
source={{ uri: 'http://10.0.2.2:8080' }}
fallbackComponent={<FallbackComponent />}
onAction={handleAction}
/>;
```

Action emitted contains action type and payload.

```tsx
// Example Server Component

const ExampleServerComponent = ({
onAction,
}: {
onAction: (action: any, payload: Record<string, any>) => void;
}) => {
const onPress = useCallback(() => {
if (onAction) {
onAction('NAVIGATE', { route: 'DetailsScreen' });
}
}, [onAction]);

return (
<View>
<Pressable onPress={onPress}>
<View>
<Text> {`Navigation`} </Text>
</View>
</Pressable>
</View>
);
};
```

## 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 for cache burst in app session.

## Running example app

TODO:: Add documentation

## Contributing

See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
Expand Down
Binary file added 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.
8 changes: 6 additions & 2 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"ios": "react-native run-ios",
"start": "react-native start",
"build:android": "cd android && ./gradlew assembleDebug --no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a",
"build:ios": "cd ios && xcodebuild -workspace ServerComponentExample.xcworkspace -scheme ServerComponentExample -configuration Debug -sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO"
"build:ios": "cd ios && xcodebuild -workspace ServerComponentExample.xcworkspace -scheme ServerComponentExample -configuration Debug -sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO",
"start:server": "node server/server.js",
"transpile:mock": "npx babel --presets=@babel/preset-env,@babel/preset-react ./server/Mocks/ExampleServerComponent.tsx -o ./server/Mocks/TranspiledExample.js"
},
"dependencies": {
"@react-navigation/native": "^6.1.17",
Expand All @@ -25,7 +27,9 @@
"@react-native/babel-preset": "0.73.21",
"@react-native/metro-config": "0.73.5",
"@react-native/typescript-config": "0.73.1",
"babel-plugin-module-resolver": "^5.0.0"
"babel-plugin-module-resolver": "^5.0.0",
"chalk": "^4.1.0",
"express": "^4.17.1"
},
"engines": {
"node": ">=18"
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ var _react = _interopRequireWildcard(require('react'));
var _reactNative = require('react-native');
var _this = void 0,

Check warning on line 11 in example/server/Mocks/TranspiledExample.js

View workflow job for this annotation

GitHub Actions / lint

Expected 'undefined' and instead saw 'void'
_jsxFileName =
'/Users/kunal.chavhan/workplace/react-native-server-component/server/Mocks/ExampleServerComponent.tsx';
'/Users/kunal.chavhan/workplace/react-native-server-component/example/server/Mocks/ExampleServerComponent.tsx';
function _getRequireWildcardCache(e) {
if ('function' != typeof WeakMap) return null;

Check warning on line 15 in example/server/Mocks/TranspiledExample.js

View workflow job for this annotation

GitHub Actions / lint

Expected literal to be on the right side of !=

Check warning on line 15 in example/server/Mocks/TranspiledExample.js

View workflow job for this annotation

GitHub Actions / lint

Expected '!==' and instead saw '!='
var r = new WeakMap(),
Expand Down
16 changes: 3 additions & 13 deletions server/server.js → example/server/server.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const express = require('express');
const fs = require('fs');
const chalk = require('chalk');

const path = require('path');
const app = express();

const mocks = path.resolve(`./server/Mocks`, 'TranspiledExample.js');
const PORT = 8080;

app.get('/', (req, res) => {
Expand All @@ -21,17 +21,7 @@ app.listen(PORT, () => {

const serverTranspiledJS = async (req, res) => {
try {
// const data = child_process
// .execSync(
// `npx babel --presets=@babel/preset-env,@babel/preset-react /Users/kunal.chavhan/workplace/RNPlayground/src/rsc/ContestCard/ContestCard.tsx`,
// )
// .toString();

const data = fs.readFileSync(
'/Users/kunal.chavhan/workplace/react-native-server-component/server/Mocks/TranspiledExample.js',
{ encoding: 'utf8', flag: 'r' }
);

const data = fs.readFileSync(mocks, { encoding: 'utf8', flag: 'r' });
return res.send(data);
} catch (e) {
console.log(chalk.red.bold`Got error ${e}`);
Expand Down
14 changes: 7 additions & 7 deletions example/src/screens/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import React, { useCallback } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { ServerComponent } from 'react-native-server-component';

const FallbackComponent = () => {
return (
<View>
<Text> Fallback Component </Text>
</View>
);
};
export default function HomeScreen({ navigation }) {

Check failure on line 12 in example/src/screens/HomeScreen.tsx

View workflow job for this annotation

GitHub Actions / lint

Binding element 'navigation' implicitly has an 'any' type.
const handleAction = useCallback(
(action: string, payload: Record<string, any>) => {
Expand All @@ -14,13 +21,6 @@ export default function HomeScreen({ navigation }) {
[navigation]
);

const FallbackComponent = () => {
return (
<View>
<Text> Fallback Component </Text>
</View>
);
};
return (
<View style={styles.container}>
<ServerComponent
Expand Down
Loading

0 comments on commit 3234ca1

Please sign in to comment.