Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: update documentation #7

Merged
merged 11 commits into from
Apr 18, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@
var _interopRequireDefault = require('@babel/runtime/helpers/interopRequireDefault');
var _typeof = require('@babel/runtime/helpers/typeof');
Object.defineProperty(exports, '__esModule', { value: true });
exports['default'] = void 0;

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

View workflow job for this annotation

GitHub Actions / lint

["default"] is better written in dot notation

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

View workflow job for this annotation

GitHub Actions / lint

Expected 'undefined' and instead saw 'void'
var _slicedToArray2 = _interopRequireDefault(
require('@babel/runtime/helpers/slicedToArray')
);
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(),
t = new WeakMap();
return (_getRequireWildcardCache = function _getRequireWildcardCache(e) {

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

View workflow job for this annotation

GitHub Actions / lint

'_getRequireWildcardCache' is a function

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

View workflow job for this annotation

GitHub Actions / lint

'_getRequireWildcardCache' is already declared in the upper scope on line 14 column 10

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

View workflow job for this annotation

GitHub Actions / lint

'e' is already declared in the upper scope on line 14 column 35
return e ? t : r;
})(e);
}
function _interopRequireWildcard(e, r) {
if (!r && e && e.__esModule) return e;
if (null === e || ('object' != _typeof(e) && 'function' != typeof e))

Check warning on line 24 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 24 in example/server/Mocks/TranspiledExample.js

View workflow job for this annotation

GitHub Actions / lint

Expected literal to be on the right side of !=
return { default: e };
var t = _getRequireWildcardCache(r);
if (t && t.has(e)) return t.get(e);
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,7 +2,14 @@
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>) => {
switch (action) {
Expand All @@ -14,13 +21,6 @@
[navigation]
);

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