Current behavior
According to hooks.js, useAsyncStorage is a wrapper rather than a hook.
// return a new object at every call
export function useAsyncStorage(key: string): AsyncStorageHook {
return {
getItem: (...args) => AsyncStorage.getItem(key, ...args), // assign a new prop at every call
setItem: (...args) => AsyncStorage.setItem(key, ...args),
mergeItem: (...args) => AsyncStorage.mergeItem(key, ...args),
removeItem: (...args) => AsyncStorage.removeItem(key, ...args),
};
}
It will either trigger unexpected re-rendring:
const MyComponent = () => {
const wrapper = useAsyncStorage('mykey');
React.useEffect(() => {
wrapper.getItem().then(res => {
//...
})
}, [wrapper]); // <------ wrapper changes at every re-rendering
}
const MyComponent = () => {
const { getItem } = useAsyncStorage('mykey');
React.useEffect(() => {
getItem().then(res => {
//...
})
}, [getItem]); // <------ getItem changes at every re-rendering
}
Or eslint hooks warnings:
const MyComponent = () => {
const wrapper = useAsyncStorage('mykey');
React.useEffect(() => {
wrapper.getItem().then(res => {
//...
})
}, []); // <------ react-hooks/exhaustive-deps warning
}
const wrapper = useAsyncStorage('mykey'); // <------ react-hooks/rules-of-hooks warning
const MyComponent = () => {
React.useEffect(() => {
wrapper.getItem().then(res => {
//...
})
}, []);
}
A workaround is to disable warnings:
// eslint-disable-next-line react-hooks/rules-of-hooks
const wrapper = useAsyncStorage('mykey');
const MyComponent = () => {
React.useEffect(() => {
wrapper.getItem().then(res => {
//...
})
}, []);
}
Expected behavior
- If
useAsyncStorage is not a standard hook, it should be renamed to something like withAsyncStorage rather than starting with use which will be treated as a hook.
export function withAsyncStorage(key: string): AsyncStorageHook {
return {
getItem: (...args) => AsyncStorage.getItem(key, ...args),
setItem: (...args) => AsyncStorage.setItem(key, ...args),
mergeItem: (...args) => AsyncStorage.mergeItem(key, ...args),
removeItem: (...args) => AsyncStorage.removeItem(key, ...args),
};
}
- If
useAsyncStorage is designed to be a standard hook, the returned value should be memo-ed
export function useAsyncStorage(key: string): AsyncStorageHook {
return React.useMemo(() => ({
getItem: (...args) => AsyncStorage.getItem(key, ...args),
setItem: (...args) => AsyncStorage.setItem(key, ...args),
mergeItem: (...args) => AsyncStorage.mergeItem(key, ...args),
removeItem: (...args) => AsyncStorage.removeItem(key, ...args),
}), []);
}
- This should be documented
Repro steps
Environment
- Platforms tested:
- AsyncStorage version:
- Expo version:
- Environment:
- Logs/Errors that may be relevant:
Current behavior
According to hooks.js,
useAsyncStorageis a wrapper rather than a hook.It will either trigger unexpected re-rendring:
Or eslint hooks warnings:
A workaround is to disable warnings:
Expected behavior
useAsyncStorageis not a standard hook, it should be renamed to something likewithAsyncStoragerather than starting withusewhich will be treated as a hook.useAsyncStorageis designed to be a standard hook, the returned value should bememo-edRepro steps
Environment