Установка состояния вместе с AsyncStorage в хуке useEffect вызывает бесконечное l oop? - PullRequest
2 голосов
/ 20 февраля 2020

Я новичок в hooks и недавно начал использовать хуки в моих проектах React Native.

Я создаю простое приложение todo, используя AsyncStorage. Сначала я инициализирую исходное состояние data и setData, используя useState hook:

const [data, setData] = useState([]);

Есть две textInput и кнопка отправки, которые я использую для сохранения данных в AsyncStorage. Вот функция saveData:

const saveData = async () => {
  const arrData = [{ name: 'vikrant', phone: 123456 }]; // [{ name, phone}] from the textInput

  const storedData = await AsyncStorage.getItem('user');
  const storedDataParsed = JSON.parse(storedData);

  let newData = [];

  if (storedData === null) {
    // save
    await AsyncStorage.setItem('user', JSON.stringify(arrData));
  } else {
    newData = [...storedDataParsed, user];
    await AsyncStorage.setItem('user', JSON.stringify(newData));
  }
  setName('');
  setPhone('');
  Keyboard.dismiss();
};

Теперь я использую useEffect, чтобы получить данные из AsyncStorage и установить их в состояние data. Я использую данные для рендеринга текста на экране.

useEffect(() => {
  retrieveData();
}, [data]);

const retrieveData = async () => {
  try {
    const valueString = await AsyncStorage.getItem('user');
    const value = JSON.parse(valueString);
    setData(value);
  } catch (error) {
    console.log(error);
  }
};

Я использую [data] в useEffect, так как я хочу перерисовывать свой компонент каждый раз, когда меняются данные, т.е. каждый раз, когда я сохраняю данные в AsyncStorage. Но это вызывает бесконечное l oop, так как setData заставляет useEffect работать бесконечно.

Если я удаляю data из [], это не l oop, но мои данные в рендере один шаг позади. Поэтому всякий раз, когда я сохраняю данные, отображаются не текущие данные, а предыдущие.

Любое объяснение того, что я делаю здесь неправильно и как я могу это исправить?

Спасибо.

Ответы [ 2 ]

2 голосов
/ 20 февраля 2020

Как уже упоминалось вами, бесконечный l oop обусловлен тем фактом, что вы передаете data как зависимость useEffect, а также устанавливаете внутри функцию, вызываемую в useEffect.

Решение здесь состоит в том, чтобы не использовать useEffect и вместо него setData всякий раз, когда вы устанавливаете значение в AsyncStorage

const saveData = async () => {
  const arrData = [{ name: 'vikrant', phone: 123456 }]; // [{ name, phone}] from the textInput

  const storedData = await AsyncStorage.getItem('user');
  const storedDataParsed = JSON.parse(storedData);

  let newData = [];

  if (storedData === null) {
    // save
    await AsyncStorage.setItem('user', JSON.stringify(arrData));
  } else {
    newData = [...storedDataParsed, user];
    await AsyncStorage.setItem('user', JSON.stringify(newData));
  }
  setName('');
  setPhone('');
  setData(newData);
  Keyboard.dismiss();
};
1 голос
/ 20 февраля 2020

Просто добавьте условный флаг retrieve для переноса asyn c хранилище, retrieveData(), вызовы.

Также в контексте "сохранения данных" я бы, вероятно, просто выделил asyn c storage-i sh logi c с состоянием logi c. Ток saveData загрязнен как состоянием, так и асинхронными c логами хранилища c.

Что-то вроде:

const [retrieve, setRetrieve] = useState(false);

// Pure AsyncStorage context
const saveData = async () => {
  ...
  if (storedData === null) {
    await AsyncStorage.setItem('user', JSON.stringify(arrData));
  } else {
    newData = [...storedDataParsed, user];
    await AsyncStorage.setItem('user', JSON.stringify(newData));
  }
  // XXX: Removed state logic, call it somewhere else.
};

const someHandler = async () => {
  await saveData();
  setRetrieve(true); // to signal effect to call retrieveData()
}

Тогда цель эффекта - просто запустить retrieveData() после сохранения.

const [data, setData] = useState([]);

useEffect(() => {
  const retrieveData = async () => {
    try {
      const valueString = await AsyncStorage.getItem('user');
      const value = JSON.parse(valueString);
      // Other set states
      setData(value);
    } catch (error) {
      console.log(error);
    }
  };
  // Retrieve if has new data
  if (retrieve)
    retrieveData();
    setRetrieve(false);
  }
}, [retrieve]);
...