| 
									
										
										
										
											2023-01-05 23:33:49 +00:00
										 |  |  | --- | 
					
						
							|  |  |  | title: React Native | 
					
						
							| 
									
										
										
										
											2023-01-10 00:31:37 +00:00
										 |  |  | pagination_prev: demos/frontend/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> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | </td><td> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | </td></tr></tbody></table> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## 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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 0) **Follow the official React Native CLI Guide!** | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Development Environment Guide: <https://reactnative.dev/docs/environment-setup> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Follow the instructions for iOS and for Android.  They will cover installation | 
					
						
							|  |  |  | and system configuration.  By the end, you should be able to run the sample app | 
					
						
							|  |  |  | in the Android and the iOS simulators. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 1) Create project: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | npm i -S react-native-table-component react-native-document-picker | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - 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: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```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 | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | adb exec-out run-as com.sheetjsrn cat files/sheetjsw.xlsx > /tmp/sheetjsw.xlsx | 
					
						
							|  |  |  | npx xlsx-cli /tmp/sheetjsw.xlsx | 
					
						
							|  |  |  | ``` |