2023-01-05 23:33:49 +00:00
|
|
|
---
|
|
|
|
title: React Native
|
2023-02-28 11:40:44 +00:00
|
|
|
pagination_prev: demos/static/index
|
2023-01-05 23:33:49 +00:00
|
|
|
pagination_next: demos/desktop/index
|
|
|
|
sidebar_position: 1
|
|
|
|
sidebar_custom_props:
|
|
|
|
summary: React + Native Rendering
|
|
|
|
---
|
|
|
|
|
|
|
|
import Tabs from '@theme/Tabs';
|
|
|
|
import TabItem from '@theme/TabItem';
|
|
|
|
|
|
|
|
The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
|
|
|
|
from the main `App.js` entrypoint or any script in the project.
|
|
|
|
|
|
|
|
The "Complete Example" creates an app that looks like the screenshots below:
|
|
|
|
|
|
|
|
<table><thead><tr>
|
|
|
|
<th><a href="#demo">iOS</a></th>
|
|
|
|
<th><a href="#demo">Android</a></th>
|
|
|
|
</tr></thead><tbody><tr><td>
|
|
|
|
|
|
|
|
![iOS screenshot](pathname:///mobile/rnios3.png)
|
|
|
|
|
|
|
|
</td><td>
|
|
|
|
|
|
|
|
![Android screenshot](pathname:///mobile/rnand3.png)
|
|
|
|
|
|
|
|
</td></tr></tbody></table>
|
|
|
|
|
2023-04-24 08:50:42 +00:00
|
|
|
["Fetching Remote Data"](#fetching-remote-data) uses the built-in `fetch` to
|
|
|
|
download and parse remote workbook files.
|
|
|
|
|
|
|
|
["Native Libraries"](#native-libraries) uses native libraries to read and write
|
|
|
|
files in the local device.
|
|
|
|
|
|
|
|
:::caution
|
|
|
|
|
|
|
|
**Before reading this demo, follow the official React Native CLI Guide!**
|
|
|
|
|
|
|
|
Development Environment Guide: <https://reactnative.dev/docs/environment-setup>
|
|
|
|
|
|
|
|
Follow the instructions for iOS (requires macOS) and for Android. They will
|
|
|
|
cover installation and system configuration. You should be able to build and run
|
|
|
|
a sample app in the Android and the iOS (if applicable) simulators.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
## Fetching Remote Data
|
|
|
|
|
|
|
|
:::info
|
|
|
|
|
|
|
|
React Native `0.72.0` will support binary data with `fetch`. For older versions,
|
|
|
|
[a native library](#native-libraries) can provide support.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
|
|
|
|
React Native 0.72.0 will support binary data with `fetch`:
|
|
|
|
|
|
|
|
```js
|
|
|
|
/* fetch data into an ArrayBuffer */
|
|
|
|
const ab = await (await fetch("https://sheetjs.com/pres.numbers")).arrayBuffer();
|
|
|
|
/* parse data */
|
|
|
|
const wb = XLSX.read(ab);
|
|
|
|
```
|
|
|
|
|
|
|
|
### Fetch Demo
|
|
|
|
|
|
|
|
The following demo uses `react-native-table-component` to display the first
|
|
|
|
worksheet in a simple table.
|
|
|
|
|
|
|
|
:::note
|
|
|
|
|
|
|
|
This demo was tested on an Intel Mac on 2023 April 24 with RN `0.72.0-rc.1`.
|
|
|
|
|
|
|
|
The iOS simulator runs iOS 16.2 on an iPhone SE (3rd generation).
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
1) Create project:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npx react-native init SheetJSRNFetch --version="0.72.0-rc.1"
|
|
|
|
```
|
|
|
|
|
|
|
|
2) Install shared dependencies:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
cd SheetJSRNFetch
|
|
|
|
curl -LO https://oss.sheetjs.com/assets/img/logo.png
|
|
|
|
npm i -S https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
|
|
|
|
npm i -S react-native-table-component@1.2.0 @types/react-native-table-component
|
|
|
|
```
|
|
|
|
|
|
|
|
Refresh iOS project by running `pod install` from the `ios` subfolder:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
cd ios; pod install; cd ..
|
|
|
|
```
|
|
|
|
|
|
|
|
3) Download [`App.tsx`](pathname:///reactnative/App.tsx) and replace:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
curl -LO https://docs.sheetjs.com/reactnative/App.tsx
|
|
|
|
```
|
|
|
|
|
|
|
|
**iOS Testing**
|
|
|
|
|
|
|
|
Start the iOS emulator:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npx react-native run-ios
|
|
|
|
```
|
|
|
|
|
|
|
|
When opened, the app should look like the "Before" screenshot below. After
|
|
|
|
tapping "Import data from a spreadsheet", the app should show new data:
|
|
|
|
|
|
|
|
<table><thead><tr>
|
|
|
|
<th>Before</th>
|
|
|
|
<th>After</th>
|
|
|
|
</tr></thead><tbody><tr><td>
|
|
|
|
|
|
|
|
![before screenshot](pathname:///reactnative/iosfetch1.png)
|
|
|
|
|
|
|
|
</td><td>
|
|
|
|
|
|
|
|
![after screenshot](pathname:///reactnative/iosfetch2.png)
|
|
|
|
|
|
|
|
</td></tr></tbody></table>
|
|
|
|
|
|
|
|
**Android Testing**
|
|
|
|
|
|
|
|
Start the Android emulator:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npx react-native run-android
|
|
|
|
```
|
|
|
|
|
|
|
|
:::note
|
|
|
|
|
|
|
|
When this demo was last tested, the simulator failed with the message
|
|
|
|
|
|
|
|
> Unable to load script. Make sure you're either Running Metro ...
|
|
|
|
|
|
|
|
The workaround is to launch Metro directly:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npx react-native start
|
|
|
|
```
|
|
|
|
|
|
|
|
Press `a` in the terminal window and Metro will try to reload the app.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
|
|
|
|
When opened, the app should look like the "Before" screenshot below. After
|
|
|
|
tapping "Import data from a spreadsheet", the app should show new data:
|
|
|
|
|
|
|
|
<table><thead><tr>
|
|
|
|
<th>Before</th>
|
|
|
|
<th>After</th>
|
|
|
|
</tr></thead><tbody><tr><td>
|
|
|
|
|
|
|
|
![before screenshot](pathname:///reactnative/andfetch1.png)
|
|
|
|
|
|
|
|
</td><td>
|
|
|
|
|
|
|
|
![after screenshot](pathname:///reactnative/andfetch2.png)
|
|
|
|
|
|
|
|
</td></tr></tbody></table>
|
|
|
|
|
2023-01-05 23:33:49 +00:00
|
|
|
|
|
|
|
## Native Libraries
|
|
|
|
|
|
|
|
:::warning
|
|
|
|
|
|
|
|
React Native does not provide a native file picker or a method for reading and
|
|
|
|
writing data from documents on the devices. A third-party library must be used.
|
|
|
|
|
|
|
|
Since React Native internals change between releases, libraries may only work
|
|
|
|
with specific versions of React Native. Project documentation should be
|
|
|
|
consulted before picking a library.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
The following table lists tested file plugins. "OS" lists tested platforms
|
|
|
|
("A" for Android and "I" for iOS). "Copy" indicates whether an explicit copy
|
|
|
|
is needed (file picker copies to cache directory and file plugin reads cache).
|
|
|
|
|
|
|
|
| File system Plugin | File Picker Plugin | OS | Copy |
|
|
|
|
|:---------------------------|:-------------------------------|:----:|:-----|
|
|
|
|
| `react-native-file-access` | `react-native-document-picker` | `AI` | |
|
|
|
|
| `react-native-blob-util` | `react-native-document-picker` | `AI` | YES |
|
|
|
|
| `rn-fetch-blob` | `react-native-document-picker` | `AI` | YES |
|
|
|
|
| `react-native-fs` | `react-native-document-picker` | `AI` | YES |
|
|
|
|
| `expo-file-system` | `expo-document-picker` | ` I` | YES |
|
|
|
|
|
|
|
|
### RN File Picker
|
|
|
|
|
|
|
|
The following libraries have been tested:
|
|
|
|
|
|
|
|
#### `react-native-document-picker`
|
|
|
|
|
|
|
|
<details open><summary><b>Selecting a file</b> (click to show)</summary>
|
|
|
|
|
|
|
|
When a copy is not needed:
|
|
|
|
|
|
|
|
```js
|
|
|
|
import { pickSingle } from 'react-native-document-picker';
|
|
|
|
|
|
|
|
const f = await pickSingle({allowMultiSelection: false, mode: "open" });
|
|
|
|
const path = f.uri; // this path can be read by RN file plugins
|
|
|
|
```
|
|
|
|
|
|
|
|
When a copy is needed:
|
|
|
|
|
|
|
|
```js
|
|
|
|
import { pickSingle } from 'react-native-document-picker';
|
|
|
|
|
|
|
|
const f = await pickSingle({allowMultiSelection: false, copyTo: "cachesDirectory", mode: "open" });
|
|
|
|
const path = f.fileCopyUri; // this path can be read by RN file plugins
|
|
|
|
```
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
#### `expo-document-picker`
|
|
|
|
|
|
|
|
<details><summary><b>Selecting a file</b> (click to show)</summary>
|
|
|
|
|
|
|
|
When using `DocumentPicker.getDocumentAsync`, enable `copyToCacheDirectory`:
|
|
|
|
|
|
|
|
```js
|
|
|
|
import * as DocumentPicker from 'expo-document-picker';
|
|
|
|
|
|
|
|
const result = await DocumentPicker.getDocumentAsync({
|
|
|
|
// highlight-next-line
|
|
|
|
copyToCacheDirectory: true,
|
|
|
|
type: ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
|
|
|
|
});
|
|
|
|
const path = result.uri;
|
|
|
|
```
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
|
|
|
|
### RN File Plugins
|
|
|
|
|
|
|
|
The following libraries have been tested:
|
|
|
|
|
|
|
|
#### `react-native-blob-util` and `rn-fetch-blob`
|
|
|
|
|
|
|
|
:::note Historical Context
|
|
|
|
|
|
|
|
The `react-native-fetch-blob` project was archived in 2019. At the time, there
|
|
|
|
were a number of project forks. The maintainers blessed the `rn-fetch-blob`
|
|
|
|
fork as the spiritual successor.
|
|
|
|
|
|
|
|
`react-native-blob-util` is an active fork of `rn-fetch-blob`
|
|
|
|
|
|
|
|
On the day that this demo was tested (2022 August 14), both `rn-fetch-blob` and
|
|
|
|
`react-native-blob-util` worked with the tested iOS and Android SDK versions.
|
|
|
|
The APIs are identical for the purposes of working with files.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
The `ascii` type returns an array of numbers corresponding to the raw bytes.
|
|
|
|
A `Uint8Array` from the data is compatible with the `buffer` type.
|
|
|
|
|
|
|
|
<details open><summary><b>Reading and Writing snippets</b> (click to show)</summary>
|
|
|
|
|
|
|
|
The snippets use `rn-fetch-blob`. To use `react-native-blob-util`, change the
|
|
|
|
`import` statements to load the module.
|
|
|
|
|
|
|
|
_Reading Data_
|
|
|
|
|
|
|
|
```js
|
|
|
|
import * as XLSX from "xlsx";
|
|
|
|
import RNFetchBlob from 'rn-fetch-blob'; // or react-native-blob-util
|
|
|
|
const { readFile } = RNFetchBlob.fs;
|
|
|
|
|
|
|
|
const res = await readFile(path, 'ascii');
|
|
|
|
const wb = XLSX.read(new Uint8Array(res), {type:'buffer'});
|
|
|
|
```
|
|
|
|
|
|
|
|
:::caution
|
|
|
|
|
|
|
|
On iOS, the URI from `react-native-document-picker` must be massaged:
|
|
|
|
|
|
|
|
```js
|
|
|
|
import { pickSingle } from 'react-native-document-picker';
|
|
|
|
import RNFetchBlob from 'rn-fetch-blob'; // or react-native-blob-util
|
|
|
|
const { readFile, dirs: { DocumentDir } } = RNFetchBlob.fs;
|
|
|
|
|
|
|
|
const f = await pickSingle({
|
|
|
|
// highlight-start
|
|
|
|
// Instruct the document picker to copy file to Documents directory
|
|
|
|
copyTo: "documentDirectory",
|
|
|
|
// highlight-end
|
|
|
|
allowMultiSelection: false, mode: "open" });
|
|
|
|
// highlight-start
|
|
|
|
// `f.uri` is the original path and `f.fileCopyUri` is the path to the copy
|
|
|
|
let path = f.fileCopyUri;
|
|
|
|
// iOS workaround
|
|
|
|
if (Platform.OS === 'ios') path = path.replace(/^.*\/Documents\//, DDP + "/");
|
|
|
|
// highlight-end
|
|
|
|
|
|
|
|
const res = await readFile(path, 'ascii');
|
|
|
|
```
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
_Writing Data_
|
|
|
|
|
|
|
|
```js
|
|
|
|
import * as XLSX from "xlsx";
|
|
|
|
import RNFetchBlob from 'rn-fetch-blob'; // or react-native-blob-util
|
|
|
|
const { writeFile, readFile, dirs:{ DocumentDir } } = RNFetchBlob.fs;
|
|
|
|
|
|
|
|
const wbout = XLSX.write(wb, {type:'buffer', bookType:"xlsx"});
|
|
|
|
const file = DocumentDir + "/sheetjsw.xlsx";
|
|
|
|
const res = await writeFile(file, Array.from(wbout), 'ascii');
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
#### `react-native-file-access`
|
|
|
|
|
|
|
|
The `base64` encoding returns strings compatible with the `base64` type:
|
|
|
|
|
|
|
|
<details open><summary><b>Reading and Writing snippets</b> (click to show)</summary>
|
|
|
|
|
|
|
|
_Reading Data_
|
|
|
|
|
|
|
|
```js
|
|
|
|
import * as XLSX from "xlsx";
|
|
|
|
import { FileSystem } from "react-native-file-access";
|
|
|
|
|
|
|
|
const b64 = await FileSystem.readFile(path, "base64");
|
|
|
|
/* b64 is a Base64 string */
|
|
|
|
const workbook = XLSX.read(b64, {type: "base64"});
|
|
|
|
```
|
|
|
|
|
|
|
|
_Writing Data_
|
|
|
|
|
|
|
|
```js
|
|
|
|
import * as XLSX from "xlsx";
|
|
|
|
import { Dirs, FileSystem } from "react-native-file-access";
|
|
|
|
const DDP = Dirs.DocumentDir + "/";
|
|
|
|
|
|
|
|
const b64 = XLSX.write(workbook, {type:'base64', bookType:"xlsx"});
|
|
|
|
/* b64 is a Base64 string */
|
|
|
|
await FileSystem.writeFile(DDP + "sheetjs.xlsx", b64, "base64");
|
|
|
|
```
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
#### `react-native-fs`
|
|
|
|
|
|
|
|
The `ascii` encoding returns binary strings compatible with the `binary` type:
|
|
|
|
|
|
|
|
<details open><summary><b>Reading and Writing snippets</b> (click to show)</summary>
|
|
|
|
|
|
|
|
_Reading Data_
|
|
|
|
|
|
|
|
```js
|
|
|
|
import * as XLSX from "xlsx";
|
|
|
|
import { readFile } from "react-native-fs";
|
|
|
|
|
|
|
|
const bstr = await readFile(path, "ascii");
|
|
|
|
/* bstr is a binary string */
|
|
|
|
const workbook = XLSX.read(bstr, {type: "binary"});
|
|
|
|
```
|
|
|
|
|
|
|
|
_Writing Data_
|
|
|
|
|
|
|
|
```js
|
|
|
|
import * as XLSX from "xlsx";
|
|
|
|
import { writeFile, DocumentDirectoryPath } from "react-native-fs";
|
|
|
|
|
|
|
|
const bstr = XLSX.write(workbook, {type:'binary', bookType:"xlsx"});
|
|
|
|
/* bstr is a binary string */
|
|
|
|
await writeFile(DocumentDirectoryPath + "/sheetjs.xlsx", bstr, "ascii");
|
|
|
|
```
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
#### `expo-file-system`
|
|
|
|
|
|
|
|
:::caution
|
|
|
|
|
|
|
|
Some Expo APIs return URI that cannot be read with `expo-file-system`. This
|
|
|
|
will manifest as an error:
|
|
|
|
|
|
|
|
> Unsupported scheme for location '...'
|
|
|
|
|
|
|
|
The [`expo-document-picker`](#expo-document-picker) snippet makes a local copy.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
The `EncodingType.Base64` encoding is compatible with `base64` type.
|
|
|
|
|
|
|
|
<details><summary><b>Reading and Writing snippets</b> (click to show)</summary>
|
|
|
|
|
|
|
|
_Reading Data_
|
|
|
|
|
|
|
|
Calling `FileSystem.readAsStringAsync` with `FileSystem.EncodingType.Base64`
|
|
|
|
encoding returns a promise resolving to a string compatible with `base64` type:
|
|
|
|
|
|
|
|
```js
|
|
|
|
import * as XLSX from "xlsx";
|
|
|
|
import * as FileSystem from 'expo-file-system';
|
|
|
|
|
|
|
|
const b64 = await FileSystem.readAsStringAsync(uri, { encoding: FileSystem.EncodingType.Base64 });
|
|
|
|
const workbook = XLSX.read(b64, { type: "base64" });
|
|
|
|
```
|
|
|
|
|
|
|
|
_Writing Data_
|
|
|
|
|
|
|
|
The `FileSystem.EncodingType.Base64` encoding accepts Base64 strings:
|
|
|
|
|
|
|
|
```js
|
|
|
|
import * as XLSX from "xlsx";
|
|
|
|
import * as FileSystem from 'expo-file-system';
|
|
|
|
|
|
|
|
const b64 = XLSX.write(workbook, {type:'base64', bookType:"xlsx"});
|
|
|
|
/* b64 is a Base64 string */
|
|
|
|
await FileSystem.writeAsStringAsync(FileSystem.documentDirectory + "sheetjs.xlsx", b64, { encoding: FileSystem.EncodingType.Base64 });
|
|
|
|
```
|
|
|
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
## Demo
|
|
|
|
|
|
|
|
:::note
|
|
|
|
|
|
|
|
This demo was tested on an Intel Mac on 2022 August 14 with RN `0.67.2`.
|
|
|
|
|
|
|
|
The iOS simulator runs iOS 15.5 on an iPhone 13.
|
|
|
|
|
|
|
|
The Android simulator runs Android 12 (S) Platform 31 on a Pixel 5.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
:::warning
|
|
|
|
|
|
|
|
There are many moving parts and pitfalls with React Native apps. It is strongly
|
|
|
|
recommended to follow the official React Native tutorials for iOS and Android
|
|
|
|
before approaching this demo. Details like creating an Android Virtual Device
|
|
|
|
are not covered here.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
This example tries to separate the library-specific functions.
|
|
|
|
|
|
|
|
1) Create project:
|
|
|
|
|
2023-04-24 08:50:42 +00:00
|
|
|
```bash
|
2023-01-05 23:33:49 +00:00
|
|
|
npx react-native init SheetJSRN --version="0.67.2"
|
|
|
|
```
|
|
|
|
|
|
|
|
2) Install shared dependencies:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
cd SheetJSRN
|
|
|
|
curl -LO https://oss.sheetjs.com/assets/img/logo.png
|
|
|
|
npm i -S https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
|
2023-04-24 08:50:42 +00:00
|
|
|
npm i -S react-native-table-component@1.2.0 react-native-document-picker
|
2023-01-05 23:33:49 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
Refresh iOS project by running `pod install` from the `ios` subfolder:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
cd ios
|
|
|
|
pod install
|
|
|
|
cd ..
|
|
|
|
```
|
|
|
|
|
|
|
|
3) Download [`index.js`](pathname:///mobile/index.js) and replace:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
curl -LO https://docs.sheetjs.com/mobile/index.js
|
|
|
|
```
|
|
|
|
|
|
|
|
Start the iOS emulator:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npx react-native run-ios
|
|
|
|
```
|
|
|
|
|
|
|
|
You should see the skeleton app:
|
|
|
|
|
|
|
|
![React Native iOS App](pathname:///mobile/rnios1.png)
|
|
|
|
|
|
|
|
4) Pick a filesystem library for integration:
|
|
|
|
|
|
|
|
|
|
|
|
<Tabs>
|
|
|
|
<TabItem value="RNBU" label="RNBU">
|
|
|
|
|
|
|
|
Install `react-native-blob-util` dependency:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npm i -S react-native-blob-util
|
|
|
|
```
|
|
|
|
|
|
|
|
Add the highlighted lines to `index.js`:
|
|
|
|
|
|
|
|
```js title="index.js"
|
|
|
|
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
|
|
|
|
|
|
|
|
// highlight-start
|
|
|
|
import { read, write } from 'xlsx';
|
|
|
|
import { pickSingle } from 'react-native-document-picker';
|
|
|
|
import { Platform } from 'react-native';
|
|
|
|
import RNFetchBlob from 'react-native-blob-util';
|
|
|
|
|
|
|
|
async function pickAndParse() {
|
|
|
|
/* rn-fetch-blob / react-native-blob-util need a copy */
|
|
|
|
const f = await pickSingle({allowMultiSelection: false, copyTo: "documentDirectory", mode: "open" });
|
|
|
|
let path = f.fileCopyUri;
|
|
|
|
if (Platform.OS === 'ios') path = path.replace(/^.*\/Documents\//, RNFetchBlob.fs.dirs.DocumentDir + "/");
|
|
|
|
const res = await RNFetchBlob.fs.readFile(path, 'ascii');
|
|
|
|
return read(new Uint8Array(res), {type: 'buffer'});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function writeWorkbook(wb) {
|
|
|
|
const wbout = write(wb, {type:'buffer', bookType:"xlsx"});
|
|
|
|
const file = RNFetchBlob.fs.dirs.DocumentDir + "/sheetjsw.xlsx";
|
|
|
|
await RNFetchBlob.fs.writeFile(file, Array.from(wbout), 'ascii');
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
// highlight-end
|
|
|
|
|
|
|
|
const make_width = ws => {
|
|
|
|
```
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="RNFA" label="RNFA">
|
|
|
|
|
|
|
|
Install `react-native-file-access` dependency:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npm i -S react-native-file-access
|
|
|
|
```
|
|
|
|
|
|
|
|
Add the highlighted lines to `index.js`:
|
|
|
|
|
|
|
|
```js title="index.js"
|
|
|
|
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
|
|
|
|
|
|
|
|
// highlight-start
|
|
|
|
import { read, write } from 'xlsx';
|
|
|
|
import { pickSingle } from 'react-native-document-picker';
|
|
|
|
import { Dirs, FileSystem } from 'react-native-file-access';
|
|
|
|
|
|
|
|
async function pickAndParse() {
|
|
|
|
/* react-native-file-access does not need a copy */
|
|
|
|
const f = await pickSingle({allowMultiSelection: false, mode: "open" });
|
|
|
|
const res = await FileSystem.readFile(f.uri, "base64");
|
|
|
|
return read(res, {type: 'base64'});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function writeWorkbook(wb) {
|
|
|
|
const wbout = write(wb, {type:'base64', bookType:"xlsx"});
|
|
|
|
const file = Dirs.DocumentDir + "/sheetjsw.xlsx";
|
|
|
|
await FileSystem.writeFile(file, wbout, "base64");
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
// highlight-end
|
|
|
|
|
|
|
|
const make_width = ws => {
|
|
|
|
```
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="RNFB" label="RNFB">
|
|
|
|
|
|
|
|
Install `rn-fetch-blob` dependency:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npm i -S rn-fetch-blob
|
|
|
|
```
|
|
|
|
|
|
|
|
Add the highlighted lines to `index.js`:
|
|
|
|
|
|
|
|
```js title="index.js"
|
|
|
|
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
|
|
|
|
|
|
|
|
// highlight-start
|
|
|
|
import { read, write } from 'xlsx';
|
|
|
|
import { pickSingle } from 'react-native-document-picker';
|
|
|
|
import { Platform } from 'react-native';
|
|
|
|
import RNFetchBlob from 'rn-fetch-blob';
|
|
|
|
|
|
|
|
async function pickAndParse() {
|
|
|
|
/* rn-fetch-blob / react-native-blob-util need a copy */
|
|
|
|
const f = await pickSingle({allowMultiSelection: false, copyTo: "documentDirectory", mode: "open" });
|
|
|
|
let path = f.fileCopyUri;
|
|
|
|
if (Platform.OS === 'ios') path = path.replace(/^.*\/Documents\//, RNFetchBlob.fs.dirs.DocumentDir + "/");
|
|
|
|
const res = await RNFetchBlob.fs.readFile(path, 'ascii');
|
|
|
|
return read(new Uint8Array(res), {type: 'buffer'});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function writeWorkbook(wb) {
|
|
|
|
const wbout = write(wb, {type:'buffer', bookType:"xlsx"});
|
|
|
|
const file = RNFetchBlob.fs.dirs.DocumentDir + "/sheetjsw.xlsx";
|
|
|
|
await RNFetchBlob.fs.writeFile(file, Array.from(wbout), 'ascii');
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
// highlight-end
|
|
|
|
|
|
|
|
const make_width = ws => {
|
|
|
|
```
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="RNFS" label="RNFS">
|
|
|
|
|
|
|
|
Install `react-native-fs` dependency:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npm i -S react-native-fs
|
|
|
|
```
|
|
|
|
|
|
|
|
Add the highlighted lines to `index.js`:
|
|
|
|
|
|
|
|
```js title="index.js"
|
|
|
|
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
|
|
|
|
|
|
|
|
// highlight-start
|
|
|
|
import { read, write } from 'xlsx';
|
|
|
|
import { pickSingle } from 'react-native-document-picker';
|
|
|
|
import { writeFile, readFile, DocumentDirectoryPath } from 'react-native-fs';
|
|
|
|
|
|
|
|
async function pickAndParse() {
|
|
|
|
/* react-native-fs needs a copy */
|
|
|
|
const f = await pickSingle({allowMultiSelection: false, copyTo: "cachesDirectory", mode: "open" });
|
|
|
|
const bstr = await readFile(f.fileCopyUri, 'ascii');
|
|
|
|
return read(bstr, {type:'binary'});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function writeWorkbook(wb) {
|
|
|
|
const wbout = write(wb, {type:'binary', bookType:"xlsx"});
|
|
|
|
const file = DocumentDirectoryPath + "/sheetjsw.xlsx";
|
|
|
|
await writeFile(file, wbout, 'ascii');
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
// highlight-end
|
|
|
|
|
|
|
|
const make_width = ws => {
|
|
|
|
```
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="EXPO" label="EXPO">
|
|
|
|
|
|
|
|
:::caution
|
|
|
|
|
|
|
|
At the time of testing, the `npx install-expo-modules` step breaks the Android
|
|
|
|
project. The demo works as expected on iOS.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
Install `expo-file-system` and `expo-document-picker` dependencies:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npx install-expo-modules
|
|
|
|
npm i -S expo-file-system expo-document-picker
|
|
|
|
```
|
|
|
|
|
|
|
|
Add the highlighted lines to `index.js`:
|
|
|
|
|
|
|
|
```js title="index.js"
|
|
|
|
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
|
|
|
|
|
|
|
|
// highlight-start
|
|
|
|
import { read, write } from 'xlsx';
|
|
|
|
import { getDocumentAsync } from 'expo-document-picker';
|
|
|
|
import { documentDirectory, readAsStringAsync, writeAsStringAsync } from 'expo-file-system';
|
|
|
|
|
|
|
|
async function pickAndParse() {
|
|
|
|
const result = await getDocumentAsync({copyToCacheDirectory: true});
|
|
|
|
const path = result.uri;
|
|
|
|
const res = await readAsStringAsync(path, { encoding: "base64" });
|
|
|
|
return read(res, {type: 'base64'});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function writeWorkbook(wb) {
|
|
|
|
const wbout = write(wb, {type:'base64', bookType:"xlsx"});
|
|
|
|
const file = documentDirectory + "sheetjsw.xlsx";
|
|
|
|
await writeAsStringAsync(file, wbout, { encoding: "base64" });
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
// highlight-end
|
|
|
|
|
|
|
|
const make_width = ws => {
|
|
|
|
```
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
</Tabs>
|
|
|
|
|
|
|
|
|
|
|
|
5) Refresh the app:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
cd ios
|
|
|
|
pod install
|
|
|
|
cd ..
|
|
|
|
```
|
|
|
|
|
|
|
|
Once refreshed, the development process must be restarted:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npx react-native run-ios
|
|
|
|
```
|
|
|
|
|
|
|
|
**iOS Testing**
|
|
|
|
|
|
|
|
The app can be tested with the following sequence in the simulator:
|
|
|
|
|
|
|
|
- Download <https://sheetjs.com/pres.numbers>
|
|
|
|
- In the simulator, click the Home icon to return to the home screen
|
|
|
|
- Click on the "Files" icon
|
|
|
|
- Click and drag `pres.numbers` from a Finder window into the simulator.
|
|
|
|
|
|
|
|
![save file iOS](pathname:///mobile/quasar7a.png)
|
|
|
|
|
|
|
|
- Make sure "On My iPhone" is highlighted and select "Save"
|
|
|
|
- Click the Home icon again then select the `SheetJSRN` app
|
|
|
|
- Click "Import data" and select `pres`:
|
|
|
|
|
|
|
|
![pick file iOS](pathname:///mobile/rnios2.png)
|
|
|
|
|
|
|
|
Once selected, the screen should refresh with new contents:
|
|
|
|
|
|
|
|
![read file iOS](pathname:///mobile/rnios3.png)
|
|
|
|
|
|
|
|
- Click "Export data". You will see a popup with a location:
|
|
|
|
|
|
|
|
![write file iOS](pathname:///mobile/rnios4.png)
|
|
|
|
|
|
|
|
- Find the file and verify the contents are correct:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
find ~/Library/Developer/CoreSimulator -name sheetjsw.xlsx |
|
|
|
|
while read x; do echo "$x"; npx xlsx-cli "$x"; done
|
|
|
|
```
|
|
|
|
|
|
|
|
Once testing is complete, stop the simulator and the development process.
|
|
|
|
|
|
|
|
**Android Testing**
|
|
|
|
|
|
|
|
There are no Android-specific steps. Emulator can be started with:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
npx react-native run-android
|
|
|
|
```
|
|
|
|
|
|
|
|
![React Native Android App](pathname:///mobile/rnand1.png)
|
|
|
|
|
|
|
|
The app can be tested with the following sequence in the simulator:
|
|
|
|
|
|
|
|
- Download <https://sheetjs.com/pres.numbers>
|
|
|
|
- Click and drag `pres.numbers` from a Finder window into the simulator.
|
|
|
|
- Click "Import data" and select `pres.numbers`:
|
|
|
|
|
|
|
|
![pick file Android](pathname:///mobile/rnand2.png)
|
|
|
|
|
|
|
|
Once selected, the screen should refresh with new contents:
|
|
|
|
|
|
|
|
![read file Android](pathname:///mobile/rnand3.png)
|
|
|
|
|
|
|
|
- Click "Export data". You will see a popup with a location:
|
|
|
|
|
|
|
|
![write file Android](pathname:///mobile/rnand4.png)
|
|
|
|
|
|
|
|
- Pull the file from the simulator and verify the contents:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
adb exec-out run-as com.sheetjsrn cat files/sheetjsw.xlsx > /tmp/sheetjsw.xlsx
|
|
|
|
npx xlsx-cli /tmp/sheetjsw.xlsx
|
|
|
|
```
|