AsyncStorage жалуется на несовместимость массивов и объектов во время mergeItem

Я пытаюсь выяснить почему AsyncStorage в приложении React Native отказывается делать mergeItem с моими данными. Моя функция выглядит так:

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}`)
  }
}

Когда я делаю то, что выполняет это, я получаю:

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]

Что сбивает с толку, потому что когда данные распечатаны, это объект с {...}, но AsyncStorage показывает массив с [...], Я что-то упускаю? Мне это кажется довольно глупым, и я не могу понять, как заставить RN играть хорошо.

PS. ИМО структура данных грубая, но это то, с чем я работаю. Я не решил это, и я не могу изменить это.

2 ответа

Решение

Я помню, как работал с AsyncStorageи это принципиально отличается от localStorage потому что он возвращает обещания.

Ваш код выглядит хорошо, что делает это очень запутанным, но я внезапно подозреваю, что проблема может быть из-за отсутствия await ключевое слово.

пытаться:

.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))
})

Это может быть классическая "не дожидаясь разрешения обещания", прежде чем перейти к асинхронной проблеме. Не удаляйте этот вопрос, если так. Это будет полезно для других, в том числе, вероятно, для меня в будущем.

Вот что я думаю происходит:

  1. JavaScript бросает AsyncStorage.mergeItem() в очередь функций

  2. Здесь нет await для этой функции, которая возвращает обещание

  3. Интерпретатор не ждет его разрешения и сразу переходит к следующей строке кода

  4. Следующая строка кода отсутствует, поэтому он возвращается из then() блок 0,1 мс позже (имейте в виду, что это неявно делает return undefined который внутри асинхронной функции такой же, как resolve(undefined)с тем, что он пытается разрешить всю цепочку обратно до await AsyncStorage.getItem(THINGS_STORAGE_KEY)

  5. obj получает мусор 0,1 мс после этого

  6. AsyncStorage.mergeItem() наблюдает ложное значение, когда он ожидал obj, но я не знаю наверняка. Возможно, он не выполняет шаг 5 или 6, а вместо этого обнаруживает, что mergeItem все еще ожидает, когда он пытается решить getItem цепь, в любом случае:

  7. AsyncStorage затем выдает сбивающее с толку сообщение об ошибке

То, что я в конечном итоге использовал для правильной обработки обещаний:

export const addThingToThing = async (title, thing) => {
  try {
    AsyncStorage.getItem(THINGS_STORAGE_KEY, (err, things) => {
      let subThings = []
      Object.values(JSON.parse(decks))
        .map((thing) => {
          if (Object.keys(thing)[0] === title) {
            subThings = [...Object.values(thing)[0].subThings, subThing]
          }
        })
      const obj = {
        [title]: {
          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}`)
  }
}

Я видел некоторые другие подобные примеры онлайн, которые использовали .done() это выглядело идентично тому, что я сделал, кроме этого звонка. Возможно, это тоже можно было попробовать, но это сработало для меня.

Другие вопросы по тегам