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.info(JSON.stringify(obj))
        AsyncStorage.mergeItem(THINGS_STORAGE_KEY,
          JSON.stringify(obj))
      })
  } catch (error) {
    console.info(`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. IMO структура данных грубая, но это то, с чем я работаю. Я не решался на это и не могу это изменить.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
2
0
478
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

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

пытаться:

.then(async ({ decks, subThings }) => {
  const obj = {
    ...arrToObj(JSON.parse(things)),
    [title]: {
      subThings
    }
  }
  console.info(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 выдает сбивающее с толку сообщение об ошибке

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

geoffjay 27.05.2018 18:29

Ах да. Я обновлю свой ответ, добавив в него async. Функция then() должна быть асинхронной, чтобы использовать внутри нее ожидание.

agm1984 27.05.2018 20:39

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

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.info(JSON.stringify(obj))
      AsyncStorage.mergeItem(THINGS_STORAGE_KEY,
        JSON.stringify(obj))
    })
  } catch (error) {
    console.info(`Error adding thing to thing: ${error.message}`)
  }
}

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

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