Skip to content

Commit b75dd11

Browse files
committed
fix(useAsyncStorage): make more robust
1 parent 54f5c9a commit b75dd11

File tree

5 files changed

+218
-9
lines changed

5 files changed

+218
-9
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,4 @@ buck-out/
6161

6262
# builds by bob
6363
lib/
64+
coverage

__tests__/useAsyncStorage.test.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* @format
3+
*/
4+
/* eslint-disable no-shadow */
5+
import { renderHook, act } from '@testing-library/react-hooks';
6+
7+
import AsyncStorage, { useAsyncStorage } from '../src';
8+
9+
afterEach(AsyncStorage.clear);
10+
11+
function expectStableCallbacks(result: any, rerender: () => void) {
12+
const previousFunctions = Object.values(result.current).filter(
13+
(item) => typeof item === 'function'
14+
);
15+
rerender();
16+
const nowFunctions = Object.values(result.current).filter(
17+
(item) => typeof item === 'function'
18+
);
19+
expect(nowFunctions).toHaveLength(previousFunctions.length);
20+
expect(nowFunctions).toEqual(previousFunctions);
21+
}
22+
23+
describe('useAsyncStorage', () => {
24+
it('should export only stable output', async () => {
25+
const { result, rerender } = renderHook(() => useAsyncStorage('key'));
26+
expect(result.error).toBeUndefined();
27+
expectStableCallbacks(result, rerender);
28+
});
29+
30+
it('can read/write data to/from storage', async () => {
31+
const newData = Math.floor(Math.random() * 1000).toString();
32+
const { result } = renderHook(() => useAsyncStorage('key'));
33+
34+
await result.current.setItem(newData);
35+
36+
const data = await result.current.getItem();
37+
38+
expect(data).toBe(newData);
39+
});
40+
41+
it('can remove from storage', async () => {
42+
await AsyncStorage.setItem('key', 'value');
43+
const { result } = renderHook(() => useAsyncStorage('key'));
44+
await act(() => result.current.removeItem());
45+
expect(await AsyncStorage.getItem('key')).toBeNull();
46+
});
47+
48+
it.skip('should throw when mergeItem is not supported', async () => {
49+
const { result } = renderHook(() => useAsyncStorage('key'));
50+
const mergeItem = AsyncStorage.mergeItem;
51+
delete AsyncStorage.mergeItem;
52+
expect(result.current.mergeItem({})).rejects.toThrow();
53+
AsyncStorage.mergeItem = mergeItem;
54+
});
55+
56+
it('can use merge with current data in storage', async () => {
57+
let originalPerson = {
58+
name: 'Jerry',
59+
age: 21,
60+
characteristics: {
61+
hair: 'black',
62+
eyes: 'green',
63+
},
64+
};
65+
66+
const { result } = renderHook(() => useAsyncStorage('person'));
67+
68+
await result.current.setItem(JSON.stringify(originalPerson));
69+
70+
originalPerson.name = 'Harry';
71+
originalPerson.characteristics.hair = 'red';
72+
// @ts-expect-error
73+
originalPerson.characteristics.shoeSize = 40;
74+
75+
await result.current.mergeItem(JSON.stringify(originalPerson));
76+
77+
const currentPerson = await result.current.getItem();
78+
const person = JSON.parse(currentPerson);
79+
80+
expect(person).toHaveProperty('name', 'Harry');
81+
expect(person.characteristics).toHaveProperty('hair', 'red');
82+
expect(person.characteristics).toHaveProperty('shoeSize', 40);
83+
});
84+
});

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@
7474
"@react-native-community/eslint-config": "^3.0.0",
7575
"@semantic-release/changelog": "^6.0.0",
7676
"@semantic-release/git": "^10.0.0",
77+
"@testing-library/react-hooks": "^7.0.2",
78+
"@types/jest": "^27.4.1",
7779
"@types/react": "^17.0.0",
7880
"@types/react-native": "^0.64.0",
7981
"concurrently": "^6.4.0",
@@ -99,10 +101,10 @@
99101
"npm/chalk": "^4.1.2"
100102
},
101103
"jest": {
102-
"preset": "react-native",
103104
"setupFiles": [
104105
"./example/jest.setup.js"
105-
]
106+
],
107+
"restoreMocks": true
106108
},
107109
"detox": {
108110
"test-runner": "jest",

src/hooks.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,35 @@
1+
import React from 'react';
12
import AsyncStorage from './AsyncStorage';
2-
import type { AsyncStorageHook } from './types';
33

4-
export function useAsyncStorage(key: string): AsyncStorageHook {
5-
return {
6-
getItem: (...args) => AsyncStorage.getItem(key, ...args),
7-
setItem: (...args) => AsyncStorage.setItem(key, ...args),
8-
mergeItem: (...args) =>
4+
export function useAsyncStorage(key: string) {
5+
const getItem = React.useCallback(
6+
(...args) => AsyncStorage.getItem(key, ...args),
7+
[key]
8+
);
9+
10+
const setItem = React.useCallback(
11+
//@ts-ignore
12+
(...args) => AsyncStorage.setItem(key, ...args),
13+
[key]
14+
);
15+
16+
const mergeItem = React.useCallback(
17+
(...args) =>
18+
//@ts-ignore
919
AsyncStorage.mergeItem?.(key, ...args) ??
1020
Promise.reject('Not implemented'),
11-
removeItem: (...args) => AsyncStorage.removeItem(key, ...args),
21+
[key]
22+
);
23+
24+
const removeItem = React.useCallback(
25+
(...args) => AsyncStorage.removeItem(key, ...args),
26+
[key]
27+
);
28+
29+
return {
30+
getItem,
31+
setItem,
32+
mergeItem,
33+
removeItem,
1234
};
1335
}

yarn.lock

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,13 @@
10981098
dependencies:
10991099
regenerator-runtime "^0.13.4"
11001100

1101+
"@babel/runtime@^7.12.5":
1102+
version "7.17.2"
1103+
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941"
1104+
integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==
1105+
dependencies:
1106+
regenerator-runtime "^0.13.4"
1107+
11011108
"@babel/template@^7.0.0", "@babel/template@^7.16.7", "@babel/template@^7.3.3", "@babel/template@^7.8.6":
11021109
version "7.16.7"
11031110
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
@@ -2275,6 +2282,17 @@
22752282
dependencies:
22762283
"@sinonjs/commons" "^1.7.0"
22772284

2285+
"@testing-library/react-hooks@^7.0.2":
2286+
version "7.0.2"
2287+
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz#3388d07f562d91e7f2431a4a21b5186062ecfee0"
2288+
integrity sha512-dYxpz8u9m4q1TuzfcUApqi8iFfR6R0FaMbr2hjZJy1uC8z+bO/K4v8Gs9eogGKYQop7QsrBTFkv/BCF7MzD2Cg==
2289+
dependencies:
2290+
"@babel/runtime" "^7.12.5"
2291+
"@types/react" ">=16.9.0"
2292+
"@types/react-dom" ">=16.9.0"
2293+
"@types/react-test-renderer" ">=16.9.0"
2294+
react-error-boundary "^3.1.0"
2295+
22782296
"@tootallnate/once@1":
22792297
version "1.1.2"
22802298
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
@@ -2357,6 +2375,14 @@
23572375
dependencies:
23582376
"@types/istanbul-lib-report" "*"
23592377

2378+
"@types/jest@^27.4.1":
2379+
version "27.4.1"
2380+
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d"
2381+
integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==
2382+
dependencies:
2383+
jest-matcher-utils "^27.0.0"
2384+
pretty-format "^27.0.0"
2385+
23602386
"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8":
23612387
version "7.0.9"
23622388
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
@@ -2402,13 +2428,27 @@
24022428
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df"
24032429
integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==
24042430

2431+
"@types/react-dom@>=16.9.0":
2432+
version "17.0.11"
2433+
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466"
2434+
integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==
2435+
dependencies:
2436+
"@types/react" "*"
2437+
24052438
"@types/react-native@^0.64.0":
24062439
version "0.64.19"
24072440
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.19.tgz#2b888c082ad293fa0fa6ae34c5e9457cfb38e50a"
24082441
integrity sha512-bT62QhaPvOKFGmlfURIC98ILjUDoIFrc2Bn5EzL3qciZrT91vHwkxFOkuEyQJy+DWaHCa2z3IrtIEJywkGu/Bg==
24092442
dependencies:
24102443
"@types/react" "*"
24112444

2445+
"@types/react-test-renderer@>=16.9.0":
2446+
version "17.0.1"
2447+
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.1.tgz#3120f7d1c157fba9df0118dae20cb0297ee0e06b"
2448+
integrity sha512-3Fi2O6Zzq/f3QR9dRnlnHso9bMl7weKCviFmfF6B4LS1Uat6Hkm15k0ZAQuDz+UBq6B3+g+NM6IT2nr5QgPzCw==
2449+
dependencies:
2450+
"@types/react" "*"
2451+
24122452
"@types/react@*", "@types/react@^17.0.0":
24132453
version "17.0.37"
24142454
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.37.tgz#6884d0aa402605935c397ae689deed115caad959"
@@ -2418,6 +2458,15 @@
24182458
"@types/scheduler" "*"
24192459
csstype "^3.0.2"
24202460

2461+
"@types/react@>=16.9.0":
2462+
version "17.0.39"
2463+
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce"
2464+
integrity sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==
2465+
dependencies:
2466+
"@types/prop-types" "*"
2467+
"@types/scheduler" "*"
2468+
csstype "^3.0.2"
2469+
24212470
"@types/retry@^0.12.0":
24222471
version "0.12.1"
24232472
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065"
@@ -2922,6 +2971,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.3.0:
29222971
dependencies:
29232972
color-convert "^2.0.1"
29242973

2974+
ansi-styles@^5.0.0:
2975+
version "5.2.0"
2976+
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
2977+
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
2978+
29252979
ansicolors@~0.3.2:
29262980
version "0.3.2"
29272981
resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979"
@@ -4991,6 +5045,11 @@ diff-sequences@^26.6.2:
49915045
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1"
49925046
integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==
49935047

5048+
diff-sequences@^27.5.1:
5049+
version "27.5.1"
5050+
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327"
5051+
integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==
5052+
49945053
diff@^5.0.0:
49955054
version "5.0.0"
49965055
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
@@ -7559,6 +7618,16 @@ jest-diff@^26.6.2:
75597618
jest-get-type "^26.3.0"
75607619
pretty-format "^26.6.2"
75617620

7621+
jest-diff@^27.5.1:
7622+
version "27.5.1"
7623+
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def"
7624+
integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==
7625+
dependencies:
7626+
chalk "^4.0.0"
7627+
diff-sequences "^27.5.1"
7628+
jest-get-type "^27.5.1"
7629+
pretty-format "^27.5.1"
7630+
75627631
jest-docblock@^26.0.0:
75637632
version "26.0.0"
75647633
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5"
@@ -7607,6 +7676,11 @@ jest-get-type@^26.3.0:
76077676
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0"
76087677
integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==
76097678

7679+
jest-get-type@^27.5.1:
7680+
version "27.5.1"
7681+
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1"
7682+
integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==
7683+
76107684
jest-haste-map@^26.5.2, jest-haste-map@^26.6.2:
76117685
version "26.6.2"
76127686
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa"
@@ -7670,6 +7744,16 @@ jest-matcher-utils@^26.6.2:
76707744
jest-get-type "^26.3.0"
76717745
pretty-format "^26.6.2"
76727746

7747+
jest-matcher-utils@^27.0.0:
7748+
version "27.5.1"
7749+
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab"
7750+
integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==
7751+
dependencies:
7752+
chalk "^4.0.0"
7753+
jest-diff "^27.5.1"
7754+
jest-get-type "^27.5.1"
7755+
pretty-format "^27.5.1"
7756+
76737757
jest-message-util@^26.6.2:
76747758
version "26.6.2"
76757759
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07"
@@ -10847,6 +10931,15 @@ pretty-format@^26.4.0, pretty-format@^26.5.2, pretty-format@^26.6.2:
1084710931
ansi-styles "^4.0.0"
1084810932
react-is "^17.0.1"
1084910933

10934+
pretty-format@^27.0.0, pretty-format@^27.5.1:
10935+
version "27.5.1"
10936+
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
10937+
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
10938+
dependencies:
10939+
ansi-regex "^5.0.1"
10940+
ansi-styles "^5.0.0"
10941+
react-is "^17.0.1"
10942+
1085010943
proc-log@^1.0.0:
1085110944
version "1.0.0"
1085210945
resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-1.0.0.tgz#0d927307401f69ed79341e83a0b2c9a13395eb77"
@@ -11136,6 +11229,13 @@ react-dom@17.0.2:
1113611229
object-assign "^4.1.1"
1113711230
scheduler "^0.20.2"
1113811231

11232+
react-error-boundary@^3.1.0:
11233+
version "3.1.4"
11234+
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0"
11235+
integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==
11236+
dependencies:
11237+
"@babel/runtime" "^7.12.5"
11238+
1113911239
react-error-overlay@^6.0.9:
1114011240
version "6.0.9"
1114111241
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"

0 commit comments

Comments
 (0)