Как установить массив массива из глобальной переменной в React - PullRequest
0 голосов
/ 25 февраля 2019

Я пытаюсь установить для переменной состояния значение глобальной переменной внутри componentWillMount.

Я делаю вызовы API, основываясь на интересах пользователя (используя функцию forEach), и я пытаюсь сохранить результаты в глобальной переменной, чтобы затем сохранить ее в переменной состояния (пользователь: {article}).

По какой-то причине в рендере переменная this.state.user.articles всегда пуста.Я что-то пропустил ?

Вот как я устанавливаю начальное значение:

class Home extends Component {

  constructor(props){
    super(props);
    this.state = {
        user :{
            articles: [],
        }
    }
    this.componentWillMount.bind(this);
}

Здесь я делаю вызовы API и пытаюсь использовать this.setState для обновления переменной

async componentWillMount(){
        const loggedUser = await Auth.currentAuthenticatedUser();
        const userEntry = await API.get(apiName,path + loggedUser.username);
        console.log(userEntry)
        currentInterests = userEntry.userInterests;
        currentInterests.forEach(async function (interest) {
          console.log(interest);
          let query = 'q='+interest+'&';
          let url = 'https://newsapi.org/v2/everything?' +
              query +
              'from=2019-02-22&' +
              'sortBy=popularity&' +
              'apiKey=hiddenforsecurity';

          let req = new Request(url);

            const response = await fetch(req);
            const json = await response.json();
            console.log(typeof json.articles);
            for(var key in json.articles){
                results.push(json.articles[key])
            }
            console.log(results[15]);


      });

     this.setState({
         user : {
             articles: results,
         }
     })
    }

В то время как console.log(results[15]) возвращает ожидаемый элемент, при рендеринге console.log(this.state.user.articles) из

render() {

       console.log(this.state.user.articles)
       return (
           <ul>
               {this.state.user.articles.map((article, index) => {
                   console.log(article.author)
                   return (<li key={index}>{article.author}</li>)})}
           </ul>
       );

}

возвращает пустой массив, как тот, который установлен в конструкторе, что означает, что функция

 this.setState({
     user : {
         articles: results,
     }
 })

из componentWillMount не имеет никакого эффекта.Чего мне не хватает?Я пробовал бесчисленные исправления онлайн, и ничто, кажется, не работает.

Ответы [ 2 ]

0 голосов
/ 25 февраля 2019

setState вызывается до завершения forEach, вот простая иллюстрация:

const arr = [ 1, 2, 3,4,5];

arr.forEach(async e => {
	const a = await fetch('https://jsonplaceholder.typicode.com/todos/1')
                    .then(response => response.json())
                    
	console.log(a)                   
})

console.log('after the loop')

Обновите componentWillMount для использования Promise.all, например:

async componentWillMount(){
  const loggedUser = await Auth.currentAuthenticatedUser();
  const userEntry = await API.get(apiName,path + loggedUser.username);

  currentInterests = userEntry.userInterests;

  const promises = currentInterests.map(interest => {
    let query = 'q='+interest+'&';
    let url = 'https://newsapi.org/v2/everything?' +
        query +
        'from=2019-02-22&' +
        'sortBy=popularity&' +
        'apiKey=hiddenforsecurity';

    let req = new Request(url);

    return fetch(req);
  })

  const results = await Promise.all(promises)
                               .then(res => res.map(e => e.json()))
                               .then(res => res.map(res.articles));

  this.setState({
    user : {
        articles: results,
    }
  })
}
0 голосов
/ 25 февраля 2019

Основная проблема заключается в том, что forEach не будет ожидать запуска каждого обратного вызова.В приведенном ниже примере done будет напечатан перед элементами массива (thing1, thing2, thing3).

const things = ["thing1", "thing2", "thing3"];

//https://gist.github.com/eteeselink/81314282c95cd692ea1d
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

const exampleFunction = async() => {
  things.forEach(async(thing) => {
    await delay(500);
    console.log(thing);
  });

  console.log("done");
}


exampleFunction();

В вашем примере состояние будет установлено до фактической обработки результатов.

Одним из способов избежать этого является использование for цикл, так что каждый оператор может быть await ed

const things = ["thing1", "thing2", "thing3"];

//https://gist.github.com/eteeselink/81314282c95cd692ea1d
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

const exampleFunction = async() => {

  for (let index = 0; index < things.length; index++) {
    await delay(500);
    console.log(things[index]);
  };

  console.log("done");
}

exampleFunction();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...