| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | --- | 
					
						
							|  |  |  | title: Local Storage API | 
					
						
							| 
									
										
										
										
											2023-09-18 06:44:33 +00:00
										 |  |  | pagination_prev: demos/data/index | 
					
						
							|  |  |  | pagination_next: demos/cloud/index | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | sidebar_custom_props: | 
					
						
							| 
									
										
										
										
											2023-09-18 06:44:33 +00:00
										 |  |  |   summary: Reading and writing data in an in-browser Key-Value store | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | --- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The Storage API, encompassing `localStorage` and `sessionStorage`, describes | 
					
						
							|  |  |  | simple key-value stores that only support string values and keys. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  | This demo covers two common use patterns: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - "Row Objects" shows a simple convention for loading and storing row objects | 
					
						
							|  |  |  | - "Simple Strings" discusses how to persist and recover a raw Storage | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-30 07:10:37 +00:00
										 |  |  | :::note Tested Deployments | 
					
						
							| 
									
										
										
										
											2023-09-18 06:44:33 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | Each browser demo was tested in the following environments: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | | Browser     | Date       | | 
					
						
							|  |  |  | |:------------|:-----------| | 
					
						
							| 
									
										
										
										
											2024-03-26 07:33:37 +00:00
										 |  |  | | Chrome 122  | 2024-03-25 | | 
					
						
							| 
									
										
										
										
											2024-03-14 08:25:08 +00:00
										 |  |  | | Safari 17.3 | 2024-03-12 | | 
					
						
							| 
									
										
										
										
											2023-09-18 06:44:33 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  | ## Row Objects
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Consider the following array of objects of data: | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```js | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  | [ | 
					
						
							|  |  |  |   { Name: "Barack Obama", Index: 44 }, | 
					
						
							|  |  |  |   { Name: "Donald Trump", Index: 45 }, | 
					
						
							|  |  |  |   { Name: "Joseph Biden", Index: 46 } | 
					
						
							|  |  |  | ] | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  | Storage API expects values to be strings. The simplest approach is to stringify | 
					
						
							|  |  |  | row objects using `JSON.stringify` and store using the row index as a key: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | | Key | Value                                | | 
					
						
							|  |  |  | |:---:|:-------------------------------------| | 
					
						
							|  |  |  | |  0  | `{"Name":"Barack Obama","Index":44}` | | 
					
						
							|  |  |  | |  1  | `{"Name":"Donald Trump","Index":45}` | | 
					
						
							|  |  |  | |  2  | `{"Name":"Joseph Biden","Index":46}` | | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### Importing Data
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Starting from a worksheet, `XLSX.utils.sheet_to_json` generates an array of row | 
					
						
							|  |  |  | objects.  `localStorage.setItem` will store data in Local Storage: | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```js | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  | function sheet_to_localStorage(worksheet) { | 
					
						
							|  |  |  |   const aoo = XLSX.utils.sheet_to_json(worksheet); | 
					
						
							|  |  |  |   for(let i = 0; i < aoo.length; ++i) { | 
					
						
							|  |  |  |     localStorage.setItem(i, JSON.stringify(aoo[i])); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  | #### Exporting Data
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | `localStorage.length` returns the total number of entries. A simple `for` loop | 
					
						
							|  |  |  | can cover the keys (integers from `0` to `localStorage.length - 1` inclusive) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | `localStorage.getItem` will load the stringified data from the Local Storage. A | 
					
						
							|  |  |  | new array of objects can be constructed by using `JSON.parse` and pushing to an | 
					
						
							|  |  |  | array.  `XLSX.utils.json_to_sheet` can create a new worksheet from that array: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js | 
					
						
							|  |  |  | function localStorage_to_sheet() { | 
					
						
							|  |  |  |   const aoo = []; | 
					
						
							|  |  |  |   for(let i = 0; i < localStorage.length; ++i) { | 
					
						
							|  |  |  |     aoo.push(JSON.parse(localStorage.getItem(i))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return XLSX.utils.json_to_sheet(aoo); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Live Demo
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This example will fetch <https://sheetjs.com/pres.numbers>, fill `localStorage` | 
					
						
							|  |  |  | with rows, then generate a worksheet from the rows and write to a new file. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | After saving the exported file, the Local Storage can be inspected in the | 
					
						
							|  |  |  | "Local Storage" section of the "Application" Tab of Developer Tools: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-18 06:44:33 +00:00
										 |  |  | :::caution pass | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | This example is for illustration purposes. If array of objects is available, it | 
					
						
							|  |  |  | is strongly recommended to convert that array to a worksheet directly. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-18 06:44:33 +00:00
										 |  |  | <details><summary><b>Live Demo</b> (click to show)</summary> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | ```jsx live | 
					
						
							|  |  |  | function SheetJStorage() { | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  |   const [url, setUrl] = React.useState("https://sheetjs.com/pres.numbers"); | 
					
						
							| 
									
										
										
										
											2023-07-21 09:17:32 +00:00
										 |  |  |   const set_url = (evt) => setUrl(evt.target.value); | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  |   const [out, setOut] = React.useState(""); | 
					
						
							|  |  |  |   const xport = React.useCallback(async() => { | 
					
						
							|  |  |  |     // get first worksheet data as array of objects | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  |     const wb = XLSX.read(await (await fetch(url)).arrayBuffer()); | 
					
						
							|  |  |  |     const aoo = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]); | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // reset and populate localStorage | 
					
						
							|  |  |  |     localStorage.clear(); | 
					
						
							|  |  |  |     for(var i = 0; i < aoo.length; ++i) localStorage.setItem(i, JSON.stringify(aoo[i])); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // create new array of objects from localStorage | 
					
						
							|  |  |  |     const new_aoo = []; | 
					
						
							|  |  |  |     for(var i = 0; i < localStorage.length; ++i) { | 
					
						
							|  |  |  |       const row = JSON.parse(localStorage.getItem(i)); | 
					
						
							|  |  |  |       new_aoo.push(row); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setOut(`Number of rows in LocalStorage: ${localStorage.length}`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // create and export workbook | 
					
						
							|  |  |  |     const new_ws = XLSX.utils.json_to_sheet(new_aoo); | 
					
						
							|  |  |  |     const new_wb = XLSX.utils.book_new(); | 
					
						
							|  |  |  |     XLSX.utils.book_append_sheet(new_wb, new_ws, "Sheet1"); | 
					
						
							|  |  |  |     XLSX.writeFile(new_wb, "SheetJStorage.xlsx"); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  |   return ( <> | 
					
						
							| 
									
										
										
										
											2023-02-28 11:40:44 +00:00
										 |  |  |     {out && ( <><a href={url}>{url}</a><pre>{out}</pre></> )} | 
					
						
							| 
									
										
										
										
											2023-02-24 07:46:48 +00:00
										 |  |  |     <b>URL: </b><input type="text" value={url} onChange={set_url} size="50"/> | 
					
						
							|  |  |  |     <br/><button onClick={xport}><b>Fetch!</b></button> | 
					
						
							|  |  |  |   </> ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-18 06:44:33 +00:00
										 |  |  | </details> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  | ## Simple Strings
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The ["Row Objects" approach](#row-objects) is strongly recommended when trying | 
					
						
							|  |  |  | to store or recover arrays of row objects. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | When the goal is to save an existing Storage, the general representation is an | 
					
						
							|  |  |  | array of pairs.  Consider the following data in Local Storage: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | | Key | Value     | | 
					
						
							|  |  |  | |:---:|:----------| | 
					
						
							|  |  |  | | "b" | "Logical" | | 
					
						
							|  |  |  | | "n" | "Numeric" | | 
					
						
							| 
									
										
										
										
											2023-04-09 21:13:24 +00:00
										 |  |  | | "s" | "Textual" | | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | The natural representation is an array of arrays: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js | 
					
						
							|  |  |  | [ | 
					
						
							|  |  |  |   [ "b", "Logical" ], | 
					
						
							|  |  |  |   [ "n", "Numeric" ], | 
					
						
							|  |  |  |   [ "s", "Textual" ] | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### Exporting Storage
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-18 06:44:33 +00:00
										 |  |  | :::note pass | 
					
						
							| 
									
										
										
										
											2023-04-09 21:13:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | Web Storage iteration order is not defined.  By using indices as keys, the row | 
					
						
							|  |  |  | objects approach has an ordering.  That does not apply to the general case. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-26 11:38:03 +00:00
										 |  |  | In modern browsers, `Object.entries` will generate an array of key/value pairs. | 
					
						
							|  |  |  | `XLSX.utils.aoa_to_sheet` will interpret that array as a worksheet with 2 cols: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js | 
					
						
							|  |  |  | function localStorage_to_ws() { | 
					
						
							|  |  |  |   const aoa = Object.entries(localStorage); | 
					
						
							|  |  |  |   return XLSX.utils.aoa_to_sheet(aoa); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### Importing Storage
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | In the other direction, the worksheet is assumed to store keys in column A and | 
					
						
							|  |  |  | values in column B.  `XLSX.utils.sheet_to_json` with the `header: 1` option | 
					
						
							|  |  |  | will generate key/value pairs that can be assigned to a storage: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js | 
					
						
							|  |  |  | function ws_to_localStorage(ws) { | 
					
						
							|  |  |  |   const aoa = XLSX.utils.sheet_to_json(ws, { header: 1 }); | 
					
						
							|  |  |  |   aoa.forEach(([key, val]) => localStorage.setItem(key, val)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2023-04-09 21:13:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ### Live Demo
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This example fills `localStorage` with 10 random keys and 10 random values, | 
					
						
							|  |  |  | generates a worksheet from the data and writes to a new file. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-18 06:44:33 +00:00
										 |  |  | <details><summary><b>Live Demo</b> (click to show)</summary> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 21:13:24 +00:00
										 |  |  | ```jsx live | 
					
						
							|  |  |  | function SheetJSRandomStorage() { | 
					
						
							|  |  |  |   const [out, setOut] = React.useState(""); | 
					
						
							|  |  |  |   const [rows, setRows] = React.useState([]); | 
					
						
							|  |  |  |   const xport = React.useCallback(async() => { | 
					
						
							|  |  |  |     // reset and populate localStorage | 
					
						
							|  |  |  |     localStorage.clear(); | 
					
						
							|  |  |  |     var data = []; | 
					
						
							|  |  |  |     for(let i = 0, last = 0; i < 10; ++i) { | 
					
						
							|  |  |  |       var k = ((Math.random() * 20)|0) + last; | 
					
						
							|  |  |  |       var v = (Math.random() * 16777216).toString(36); | 
					
						
							|  |  |  |       localStorage.setItem(k, v); | 
					
						
							|  |  |  |       data.push([k,v]); | 
					
						
							|  |  |  |       last = k; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     setRows(Object.entries(localStorage)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // create new worksheet from localStorage | 
					
						
							|  |  |  |     const aoa = Object.entries(localStorage); | 
					
						
							|  |  |  |     const new_ws = XLSX.utils.aoa_to_sheet(aoa); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // create and export workbook | 
					
						
							|  |  |  |     const new_wb = XLSX.utils.book_new(); | 
					
						
							|  |  |  |     XLSX.utils.book_append_sheet(new_wb, new_ws, "Sheet1"); | 
					
						
							|  |  |  |     XLSX.writeFile(new_wb, "SheetJSRandomStorage.xlsx"); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return ( <> | 
					
						
							|  |  |  |     {out && ( <><a href={url}>{url}</a><pre>{out}</pre></> )} | 
					
						
							|  |  |  |     {rows.length && (<table><tr><th>Key</th><th>Value</th></tr> | 
					
						
							|  |  |  |       {rows.map(([k,v]) => (<tr><td>{k}</td><td>{v}</td></tr>))} | 
					
						
							|  |  |  |     </table>) || null} | 
					
						
							|  |  |  |     <br/><button onClick={xport}><b>Export!</b></button> | 
					
						
							|  |  |  |   </> ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2023-09-18 06:44:33 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | </details> |