您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

Storage.js 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import AsyncStorage from '@react-native-community/async-storage';
  2. /**
  3. * A Web Sorage API implementation used for polyfilling
  4. * {@code window.localStorage} and/or {@code window.sessionStorage}.
  5. * <p>
  6. * The Web Storage API is synchronous whereas React Native's builtin generic
  7. * storage API {@code AsyncStorage} is asynchronous so the implementation with
  8. * persistence is optimistic: it will first store the value locally in memory so
  9. * that results can be served synchronously and then persist the value
  10. * asynchronously. If an asynchronous operation produces an error, it's ignored.
  11. */
  12. export default class Storage {
  13. /**
  14. * Initializes a new {@code Storage} instance. Loads all previously
  15. * persisted data items from React Native's {@code AsyncStorage} if
  16. * necessary.
  17. *
  18. * @param {string|undefined} keyPrefix - The prefix of the
  19. * {@code AsyncStorage} keys to be persisted by this storage.
  20. */
  21. constructor(keyPrefix) {
  22. /**
  23. * The prefix of the {@code AsyncStorage} keys persisted by this
  24. * storage. If {@code undefined}, then the data items stored in this
  25. * storage will not be persisted.
  26. *
  27. * @private
  28. * @type {string}
  29. */
  30. this._keyPrefix = keyPrefix;
  31. // Perform optional asynchronous initialization.
  32. const initializing = this._initializeAsync();
  33. if (initializing) {
  34. // Indicate that asynchronous initialization is under way.
  35. this._initializing = initializing;
  36. // When the asynchronous initialization completes, indicate its
  37. // completion.
  38. initializing.finally(() => {
  39. if (this._initializing === initializing) {
  40. this._initializing = undefined;
  41. }
  42. });
  43. }
  44. }
  45. /**
  46. * Removes all keys from this storage.
  47. *
  48. * @returns {void}
  49. */
  50. clear() {
  51. for (const key of Object.keys(this)) {
  52. this.removeItem(key);
  53. }
  54. }
  55. /**
  56. * Returns the value associated with a specific key in this storage.
  57. *
  58. * @param {string} key - The name of the key to retrieve the value of.
  59. * @returns {string|null} The value associated with {@code key} or
  60. * {@code null}.
  61. */
  62. getItem(key) {
  63. return this.hasOwnProperty(key) ? this[key] : null;
  64. }
  65. /**
  66. * Returns the value associated with a specific key in this {@code Storage}
  67. * in an async manner. The method is required for the cases where we need
  68. * the stored data but we're not sure yet whether this {@code Storage} is
  69. * already initialized (e.g. On app start).
  70. *
  71. * @param {string} key - The name of the key to retrieve the value of.
  72. * @returns {Promise}
  73. */
  74. _getItemAsync(key) {
  75. return (
  76. (this._initializing || Promise.resolve())
  77. .catch(() => { /* _getItemAsync should always resolve! */ })
  78. .then(() => this.getItem(key)));
  79. }
  80. /**
  81. * Performs asynchronous initialization of this {@code Storage} instance
  82. * such as loading all keys from {@link AsyncStorage}.
  83. *
  84. * @private
  85. * @returns {Promise}
  86. */
  87. _initializeAsync() {
  88. if (typeof this._keyPrefix !== 'undefined') {
  89. // Load all previously persisted data items from React Native's
  90. // AsyncStorage.
  91. return new Promise(resolve => {
  92. AsyncStorage.getAllKeys().then((...getAllKeysCallbackArgs) => {
  93. // XXX The keys argument of getAllKeys' callback may or may
  94. // not be preceded by an error argument.
  95. const keys
  96. = getAllKeysCallbackArgs[
  97. getAllKeysCallbackArgs.length - 1
  98. ].filter(key => key.startsWith(this._keyPrefix));
  99. AsyncStorage.multiGet(keys)
  100. .then((...multiGetCallbackArgs) => {
  101. // XXX The result argument of multiGet may or may not be
  102. // preceded by an errors argument.
  103. const result
  104. = multiGetCallbackArgs[
  105. multiGetCallbackArgs.length - 1
  106. ];
  107. const keyPrefixLength
  108. = this._keyPrefix && this._keyPrefix.length;
  109. // eslint-disable-next-line prefer-const
  110. for (let [ key, value ] of result) {
  111. key = key.substring(keyPrefixLength);
  112. // XXX The loading of the previously persisted data
  113. // items from AsyncStorage is asynchronous which
  114. // means that it is technically possible to invoke
  115. // setItem with a key before the key is loaded from
  116. // AsyncStorage.
  117. if (!this.hasOwnProperty(key)) {
  118. this[key] = value;
  119. }
  120. }
  121. resolve();
  122. });
  123. });
  124. });
  125. }
  126. return undefined;
  127. }
  128. /**
  129. * Returns the name of the nth key in this storage.
  130. *
  131. * @param {number} n - The zero-based integer index of the key to get the
  132. * name of.
  133. * @returns {string} The name of the nth key in this storage.
  134. */
  135. key(n) {
  136. const keys = Object.keys(this);
  137. return n < keys.length ? keys[n] : null;
  138. }
  139. /**
  140. * Returns an integer representing the number of data items stored in this
  141. * storage.
  142. *
  143. * @returns {number}
  144. */
  145. get length() {
  146. return Object.keys(this).length;
  147. }
  148. /**
  149. * Removes a specific key from this storage.
  150. *
  151. * @param {string} key - The name of the key to remove.
  152. * @returns {void}
  153. */
  154. removeItem(key) {
  155. delete this[key];
  156. typeof this._keyPrefix === 'undefined'
  157. || AsyncStorage.removeItem(`${String(this._keyPrefix)}${key}`);
  158. }
  159. /**
  160. * Adds a specific key to this storage and associates it with a specific
  161. * value. If the key exists already, updates its value.
  162. *
  163. * @param {string} key - The name of the key to add/update.
  164. * @param {string} value - The value to associate with {@code key}.
  165. * @returns {void}
  166. */
  167. setItem(key, value) {
  168. value = String(value); // eslint-disable-line no-param-reassign
  169. this[key] = value;
  170. typeof this._keyPrefix === 'undefined'
  171. || AsyncStorage.setItem(`${String(this._keyPrefix)}${key}`, value);
  172. }
  173. }