Асинхронный массив React -Native.map = undefined не является объектом

#javascript #react-native #async-await

#javascript #react-native #async-await

Вопрос:

Я пытаюсь создать свое первое гибридное приложение с ReactNative. У меня проблема с моим Array.map…

 class HomeScreen extends React.Component {

  state = {
    isLoading: false,
    captured: false,
    wished: false,
    exchanged: false,
    data: {}
  };

  async getPokemonFromApiAsync() {
    try {
      this.setState({isLoading: true});
      let response = await fetch('https://pokeapi.co/api/v2/pokemon/?limit=0amp;offset=20');
return this.setState({
        isLoading: false,
        data: await response.json()
       });
    } catch (error) {
      console.error(error);
  }

  (...)    

  componentWillMount() {
      this.getPokemonFromApiAsync()
  }

  (...)

  result = (result = this.state.data.results) => {
      console.log('test', this.state.data);
      return (
         <View>

             (...)

             result.map( (item, index) => {

             (...)

             }
         </View>
      )
  } 
}
  

Я не понимаю, почему моя функция getPokemonFromApiAsync пуста. Симулятор iOS возвращает ошибку типа: Undefined не является объектом (вычисляется ‘result.map’)

И при добавлении конструктора, подобного:

 constructor(props) {
   super(props)
   this.getPokemonFromApiAsync = This.getPokemonFromApiAsync.bind(this)
}
  

У меня много ошибок в консоли:

Предупреждение: не удается вызвать %s для компонента, который еще не смонтирован. Это сбой, но это может указывать на ошибку в вашем приложении. Вместо этого назначьте this.state напрямую или определите state = {}; свойство класса с желаемым состоянием в компоненте %s., setState, рабочий стол

Для меня это нормально…

Каков подходящий жизненный цикл для асинхронного Http-запроса?

Комментарии:

1. Отбросьте await в состояние, и все будет хорошо.

2. Спасибо. Но у меня такая же ошибка. console.log(‘test’, this.state.data) возвращает объект {}. Моя проблема заключается в том, что данные извлекаются, но не извлекаются в состоянии.

3. Ну, вам не хватает объекта конфигурации в вашем операторе выборки. убедитесь, что вы их предоставляете. Я подозреваю, что ваша выборка терпит неудачу.

Ответ №1:

Лучший способ с использованием библиотеки axios на github по ссылке

 npm install axios
  

Наконец, еженедельные загрузки превышают 4 000 000 Github запускается на 50 000

Ответ №2:

Ваша ошибка вызвана тем, как вы настроили свое начальное data состояние in.

Вы настроили его как:

 state = {
  isLoading: false,
  captured: false,
  wished: false,
  exchanged: false,
  data: {} // <- here you define it as an object, with no parameters
};
  

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

 state = {
  isLoading: false,
  captured: false,
  wished: false,
  exchanged: false,
  data: { results: [] } // <- here you should define the results inside the object
};
  

Причина, по которой вы получаете ошибку:

 TypeError: Undefined is not an object (evaluating 'result.map')
  

Это потому, что при первоначальном рендеринге, прежде чем ваш ответ на выборку вернется, он пытается map перезаписать this.state.data.results то, чего не существует. Вам нужно убедиться, что в состоянии есть начальное значение для results .

Это должно остановить начальную ошибку, однако вам нужно будет убедиться, что то, что вы сохраняете в состояние для data , также является массивом, иначе вы продолжите получать ту же ошибку.


componentWillMount устарел, и вы должны использовать componentDidMount .

Также, поскольку вы вызываете асинхронную функцию внутри себя componentWillMount , вам следует реорганизовать ее следующим образом:

 async componentDidMount() {
  await this.getPokemonFromApiAsync()
}
  

Так что монтирование не происходит до тех пор, пока не будет завершен запрос на выборку.


Я бы также провел рефакторинг вашего, getPokemonFromApiAsync чтобы вы получили response.json() , прежде чем пытаться перевести его в состояние. Вам также не нужен оператор return, поскольку this.setState ничего не возвращает.

 async getPokemonFromApiAsync() {
  try {
    this.setState({isLoading: true});
    let response = await fetch('https://pokeapi.co/api/v2/pokemon/?limit=0amp;offset=20');
    let data = await response.json(); // get the data 
    this.setState({
      isLoading: false,
      data: data // now set it to state
    });
  } catch (error) {
    console.error(error);
}
  

Перекус:

Вот очень простой пример, показывающий, как работает код https://snack.expo.io/@andypandy/pokemon-fetch

Код для snack:

 export default class App extends React.Component {

  state = {
    isLoading: false,
    captured: false,
    wished: false,
    exchanged: false,
    data: { results: [] } // <- here you should define the results inside the object
  };

  getPokemonFromApiAsync = async () => {
    try {
      this.setState({ isLoading: true });
      let response = await fetch('https://pokeapi.co/api/v2/pokemon/?limit=0amp;offset=20');
      let data = await response.json(); // get the data
      this.setState({
        isLoading: false,
        data: data // now set it to state
      });
    } catch (error) {
      console.error(error);
    }
  }

  async componentDidMount () {
    await this.getPokemonFromApiAsync();
  }

  render () {
    return (
      <View style={styles.container}>
        {this.state.data.results.map(item => <Text>{item.name}</Text>)}
      </View>
    );
  }
}
  

Комментарии:

1. Привет, посетитель из будущего, для получения дополнительной информации: facebook.github.io/react-native/docs/network#using-fetch

Ответ №3:

Лучший способ — реализовать значения вашего состояния, когда ваше обещание будет выполнено с использованием «then».

 let response = fetch('https://pokeapi.co/api/v2/pokemon/?limit=0amp;offset=20')
  .then((response) => {
     this.setState({
       isLoading: false,
       data: response.json()
     });
  });
  

Возможно, вы можете обработать свои данные ( result.map ) в обратном вызове Promise и напрямую вставить результат в свой компонент.

И, кстати, вызовы XHR обычно обрабатываются в componentDidMount методе.

Ответ №4:

Причина, по которой вы получаете ошибку TypeError: Undefined is not an object (evaluating 'result.map') , заключается result в this.state.data.results том {} , что вы получаете данные из data.result , но поскольку данные являются асинхронными, при первом рендеринге данные являются undefined (поскольку вы задаете им состояние), so .map() есть, и вы не можете использовать в неустановленном.

Чтобы решить эту проблему, вы можете проверить, не является ли data.result неопределенным, прежде чем отображать его.

   return (
     <View>

         (...)

         result amp;amp; result.map( (item, index) => {

         (...)

         }
     </View>
  )