I'm trying to figure out why AsyncStorage
in a React Native app refuses to do a mergeItem
with my data. My function looks like this:
const arrToObj = (array) =>
array.reduce((obj, item) => {
obj[Object.keys(item)[0]] = Object.values(item)[0]
return obj
}, {})
export const addThingToThing = async (title, thing) => {
try {
let subThings = []
let things = {}
await AsyncStorage.getItem(THINGS_STORAGE_KEY)
.then((things) => {
let subThings = []
Object.values(JSON.parse(decks))
.map((thing) => {
if (Object.keys(thing)[0] === title) {
subThings = [...Object.values(thing)[0].subThings, subThing]
}
})
return { decks, subThings }
})
.then(({ decks, subThings }) => {
const obj = {
...arrToObj(JSON.parse(things)),
[title]: {
subThings
}
}
console.log(JSON.stringify(obj))
AsyncStorage.mergeItem(THINGS_STORAGE_KEY,
JSON.stringify(obj))
})
} catch (error) {
console.log(`Error adding thing to thing: ${error.message}`)
}
}
When I do the thing that executes this I get:
13:35:52: {"test":{"subThings":[{"one":"a","two":"a"}]},"test2":{"title":"test2","questions":[]}}
13:35:55: [Unhandled promise rejection: Error: Value [{"test":{"title":"test","subThings":[]}},{"test2":{"title":"test2","subThings":[]}}] of type org.json.JSONArray cannot be converted to JSONObject]
Which is confusing, because when the data is printed out it's an object with {...}
, but AsyncStorage
shows an array with [...]
. Is there something I'm missing? This seems pretty dumb to me and I can't seem to figure out how to get RN to play nice.
PS. IMO the structure of the data is gross, but it's what I'm working with. I didn't decide on it, and I can't change it.
I recall working with AsyncStorage
, and it is fundamentally different than localStorage
because it returns Promises.
Your code looks fine which makes this super confusing, but I am suddenly suspecting that the problem may be due to a missing await
keyword.
try:
.then(async ({ decks, subThings }) => {
const obj = {
...arrToObj(JSON.parse(things)),
[title]: {
subThings
}
}
console.log(JSON.stringify(obj))
// We are adding await here
await AsyncStorage.mergeItem(THINGS_STORAGE_KEY, JSON.stringify(obj))
})
It may be a classic "not waiting for the Promise to resolve" before moving on, async problem. Don't delete this question if so. It will be helpful for others including probably myself in the future.
Here's what I think is happening:
JavaScript throws AsyncStorage.mergeItem()
into the function queue
There is no await
for this function that returns a Promise
The interpreter does not wait for it to resolve and immediately moves to the next line of code
There is no next line of code, so it returns from the then()
block 0.1ms later (keep in mind it is implicitly doing return undefined
which inside an async function is the same as resolve(undefined)
, with the point being that it is trying to resolve the entire chain back up to await AsyncStorage.getItem(THINGS_STORAGE_KEY)
obj
gets garbage collected 0.1ms after that
AsyncStorage.mergeItem()
observes a falsy value where it was expecting obj
, but I don't actually know for certain. It may not be doing step 5 or 6 and instead detecting that mergeItem
is still pending when it tries to resolve the getItem
chain, either way:
AsyncStorage then gives a confusing error message