21 KiB
title | pagination_prev | pagination_next | sidebar_position | sidebar_custom_props | ||
---|---|---|---|---|---|---|
React Native | demos/static/index | demos/desktop/index | 1 |
|
import current from '/version.js'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock';
The NodeJS Module 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:
iOS | Android |
---|---|
"Fetching Remote Data" uses the built-in fetch
to
download and parse remote workbook files.
"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 can provide support.
:::
React Native 0.72.0 will support binary data with fetch
:
/* 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).
:::
- Create project:
npx react-native init SheetJSRNFetch --version="0.72.0-rc.1"
- Install shared dependencies:
{\ cd SheetJSRNFetch curl -LO https://oss.sheetjs.com/assets/img/logo.png npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.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:
cd ios; pod install; cd ..
- Download
App.tsx
and replace:
curl -LO https://docs.sheetjs.com/reactnative/App.tsx
iOS Testing
Start the iOS emulator:
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:
Before | After |
---|---|
Android Testing
Start the Android emulator:
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:
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:
Before | After |
---|---|
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
Selecting a file (click to show)
When a copy is not needed:
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:
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
expo-document-picker
Selecting a file (click to show)
When using DocumentPicker.getDocumentAsync
, enable copyToCacheDirectory
:
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;
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
When this demo was last tested, rn-fetch-blob
and react-native-blob-util
both 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.
Reading and Writing snippets (click to show)
The snippets use rn-fetch-blob
. To use react-native-blob-util
, change the
import
statements to load the module.
Reading Data
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:
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
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');
react-native-file-access
The base64
encoding returns strings compatible with the base64
type:
Reading and Writing snippets (click to show)
Reading Data
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
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");
react-native-fs
The ascii
encoding returns binary strings compatible with the binary
type:
Reading and Writing snippets (click to show)
Reading Data
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
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");
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
snippet makes a local copy.
:::
The EncodingType.Base64
encoding is compatible with base64
type.
Reading and Writing snippets (click to show)
Reading Data
Calling FileSystem.readAsStringAsync
with FileSystem.EncodingType.Base64
encoding returns a promise resolving to a string compatible with base64
type:
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:
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 });
Demo
:::note
This demo was tested on an Intel Mac on 2023 April 30 with RN 0.72.0-rc.1
.
The iOS simulator runs iOS 16.2 on an iPhone 14.
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.
- Create project:
npx react-native init SheetJSRN --version="0.72.0-rc.1"
- Install shared dependencies:
{\ cd SheetJSRN curl -LO https://oss.sheetjs.com/assets/img/logo.png npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz npm i -S react-native-table-component@1.2.0 react-native-document-picker@8.2.0
}
Refresh iOS project by running pod install
from the ios
subfolder:
cd ios
pod install
cd ..
- Download
index.js
and replace:
curl -LO https://docs.sheetjs.com/mobile/index.js
Start the iOS emulator:
npx react-native run-ios
You should see the skeleton app:
- Pick a filesystem library for integration:
Install react-native-blob-util
dependency:
npm i -S react-native-blob-util@0.17.1
Add the highlighted lines to 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 (await fetch(path)).arrayBuffer(); // RN >= 0.72
// const res = await RNFetchBlob.fs.readFile(path, 'ascii'); // RN < 0.72
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 => {
Install react-native-file-access
dependency:
npm i -S react-native-file-access@2.6.0
Add the highlighted lines to 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 { Dirs, FileSystem } from 'react-native-file-access';
async function pickAndParse() {
/* react-native-file-access in RN < 0.72 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'});
/* react-native-file-access in RN >= 0.72 needs a copy */
const f = await pickSingle({allowMultiSelection: false, copyTo: "documentDirectory", mode: "open" });
let path = f.fileCopyUri;
const res = await (await fetch(path)).arrayBuffer();
return read(new Uint8Array(res), {type: 'buffer'});
}
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 => {
Install rn-fetch-blob
dependency:
npm i -S rn-fetch-blob@0.12.0
Add the highlighted lines to 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 (await fetch(path)).arrayBuffer(); // RN >= 0.72
// const res = await RNFetchBlob.fs.readFile(path, 'ascii'); // RN < 0.72
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 => {
Install react-native-fs
dependency:
npm i -S react-native-fs@2.20.0
Add the highlighted lines to 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 => {
:::warning
At the time of testing, Expo did not support RN 0.72 . The project should be created with React Native 0.67.2:
npx react-native init SheetJSRN --version="0.67.2"
:::
Install expo-file-system
and expo-document-picker
dependencies:
npx install-expo-modules
npm i -S expo-file-system expo-document-picker
Add the highlighted lines to 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 => {
- Refresh the app:
cd ios
pod install
cd ..
Once refreshed, the development process must be restarted:
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.
- 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
:
Once selected, the screen should refresh with new contents:
- Click "Export data". You will see a popup with a location:
- Find the file and verify the contents are correct:
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:
npx react-native run-android
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
:
Once selected, the screen should refresh with new contents:
- Click "Export data". You will see a popup with a location:
- Pull the file from the simulator and verify the contents:
adb exec-out run-as com.sheetjsrn cat files/sheetjsw.xlsx > /tmp/sheetjsw.xlsx
npx xlsx-cli /tmp/sheetjsw.xlsx