I'm trying to implement a custom hook, which extends the useState() by automatically saving the current state value in the Capacitor storage.
In this case I'm struggeling with the aynchronus functions of the Capacitor storage plugin, which causes that my "custom state" actually is a promise:/
Any ideas how I can fix this? I already tried to initialize value
as undefined state and extended the useCallback by the determination of the initial value. But this didnt work.
The custom hook:
import { useCallback, useState } from "react";
import { Plugins } from '@capacitor/core';
import { AnyObject } from "../models/SomeModels";
const { Storage } = Plugins;
const APP_NAME = 'de.xxx.yyy';
const getStorage = async(key: string, defaultValue: string|AnyObject) => {
const { value } = await Storage.get({
key: `${APP_NAME}.${key}`
});
let returnValue;
try {
if (value) {
returnValue = JSON.parse(value);
};
} catch(e) {
returnValue = value;
};
return returnValue || defaultValue;
};
export const useStorage = (key: string, defaultValue: string|AnyObject) => {
const [value, setValue] = useState<string|AnyObject>(async() => await getStorage(key, defaultValue));
useCallback(() => {
if (value) {
let newValue;
try {
newValue = JSON.stringify(value);
} catch(e) {
newValue = value;
};
Storage.set({
key: `${APP_NAME}.${key}`,
value: newValue as string
});
};
}, [key, value]);
return [value, setValue];
};
The component:
[...]
const [test, setTest] = useStorage('Test', '1');
[...]
return (
[...]
<IonItem>
<IonLabel>Test</IonLabel>
<IonInput value={test as string} />
</IonItem>
[...]
I got the idea for this hook from this blog.
EDIT:
By adding the change-handler (onIonChange={e => setTest(e.detail.value!)}
) for the IonInput, I recognized that setTest
also not working.
Dieser Ausdruck kann nicht aufgerufen werden. Es ist kein Bestandteil vom Typ "string | AnyObject" aufrufbar.ts(2349)
const setTest: string | AnyObject
After thinking about it again and again and again, I got it finally working after lunch.
I had to initize value
as empty state, changing the useCallback to useEffect and adding the empty case to my condition.
Maybe someone can explain, why I have to set the as const
at the end?
import { useEffect, useState } from "react";
import { Plugins } from '@capacitor/core';
import { AnyObject } from "../models/SomeModels";
const { Storage } = Plugins;
const APP_NAME = 'de.xxx.yyy';
const getStorage = async(key: string, defaultValue: string|AnyObject) => {
const { value } = await Storage.get({
key: `${APP_NAME}.${key}`
});
let returnValue;
try {
if (value) {
returnValue = JSON.parse(value);
};
} catch(e) {
returnValue = value;
};
return returnValue || defaultValue;
};
export const useStorage = (key: string, defaultValue: string|AnyObject) => {
const [value, setValue] = useState<string|AnyObject>();
useEffect(() => {
if (value || typeof value === 'string') {
let newValue;
try {
newValue = JSON.stringify(value);
} catch(e) {
newValue = value;
};
Storage.set({
key: `${APP_NAME}.${key}`,
value: newValue as string
});
} else {
getStorage(key, defaultValue).then(v => setValue(v));
};
}, [key, value, defaultValue]);
return [value, setValue] as const;
};