Визуализация выполняется до завершения асинхронного метода.Как я могу передать реквизиты компоненту из моего асинхронного метода? - PullRequest
0 голосов
/ 19 февраля 2019

Я хочу передать данные из firestore в мои компоненты.Но запрос асинхронный, поэтому я застрял на проблеме, когда мой метод рендеринга работает, прежде чем я получу данные из моего запроса.Поэтому, когда рендер запускается, значение pops равно Null, потому что состояние еще не определено.Также компонент почему-то рендерит дважды.Есть ли лучший способ справиться с асинхронными вещами?

Уже пытался положить запрос на пожарное хранилище на более высоком месте (index.js) и передать данные в приложение.Пытался сделать запрос внутри метода рендеринга.Пытался поместить запрос в асинхронную функцию и вызвать его в конструкторе, чтобы установить там состояние.То же самое, что и вызов асинхронного метода.

class App extends Component {
constructor(props) {
    super(props);
    this.giveUsername = this.giveUsername.bind(this);

    const userId = firebase.auth().currentUser.email;
    this.giveUsername(userId);
}

async giveUsername(userId){
    let username;
    await firebase.firestore().collection('users').where('email', '==', userId).get().then((snapshot) =>{
        snapshot.docs.forEach(doc =>{
            console.log(`doc.data().username - ${doc.data().username}`);
            username = doc.data().username;
        });
    });
    this.setState( {
        userId : userId,
        username : username
    });
 }

render() {
    return (
      <div className='App'>
        <Header username={this.state.username}/>
        <NewTask userId={this.state.userId}/>
        <TaskList userId={this.state.userId}/>
      </div>
    );
}
}

Ответы [ 4 ]

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

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

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

constructor(){
  this.state = {username: null, userId: null, loaded: false};
}
render() {
    return (
      <div className='App'>
        {
           !this.state.loaded ? <p>Loading...</p> : 

              <Header username={this.state.username}/>
              <NewTask userId={this.state.userId}/>
              <TaskList userId={this.state.userId}/>
        }
      </div>
    );
}

Не выполняйте запросы на выборку в вашем контроллере!Используйте componentDidMount

componentDidMount(){
    const userId = firebase.auth().currentUser.email;
    this.giveUsername(userId);
}

Затем, наконец, в giveUsername

async giveUsername(userId){
    let username;
    await firebase.firestore().collection('users').where('email', '==', userId).get().then((snapshot) =>{
        snapshot.docs.forEach(doc =>{
            console.log(`doc.data().username - ${doc.data().username}`);
            username = doc.data().username;
        });
    });
    this.setState( {
        userId : userId,
        username : username,
        loaded: true
    });
 }
0 голосов
/ 19 февраля 2019

Рендер будет запущен до того, как вы получите ваши данные, это абсолютно нормально и правильно.Проверьте, равны ли данные null, и отобразите загрузочное сообщение или ничего (return null)

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

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

constructor(props) {
    super(props);
    state = {
      userId : 0,
      username : '',
      loading: true
    }
}

установите loading на false, когда данные готовы:

async giveUsername(userId){
    ...
    this.setState( {
        userId : userId,
        username : username,
        loading: false
    });
 }

Визуализация:

render() {
    return (
      <div className='App'>
        {this.state.loading 
         ? <span>Loading ... </span> 
         : (
             <Header username={this.state.username}/>
             <NewTask userId={this.state.userId}/>
             <TaskList userId={this.state.userId}/>
           )
        }
      </div>
    );
}

, и причина, по которой компонент выполняет рендеринг, заключается в том, чтопотому что state меняется, и это нормально.

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

Обычно принято вызывать функции, которые будут устанавливать ваши компоненты с componentDidMount(), а не с constructor() метода.

Кроме того, async await в вашем *Функция 1008 * не ожидает Promise.then().Решение состоит в том, чтобы использовать либо , а не оба.

Установка начального значения для this.state также рекомендуется при работе с неопределенными данными.

См. Ниже для получения информации опрактический пример.

class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      username: undefined,
      userId: undefined
    };
    this.giveUsername = this.giveUsername.bind(this);
  }

  render() {
    return (
      <div className="App">
        <Header username={this.state.username}/>
        <NewTask userId={this.state.userId}/>
        <TaskList userId={this.state.userId}/>
      </div>
    );
  }

  componentDidMount() {
    const userId = firebase.auth().currentUser.email;
    this.giveUsername(userId);
  }

  async giveUsername(userId) {
    let username;
    const snapshot = await firebase.firestore().collection('users').where('email', '==', userId).get();
    snapshot.docs.forEach(doc => {
      console.log(`doc.data().username - ${doc.data().username}`);
      username = doc.data().username;
    });
    this.setState({userId : userId, username : username});
  }

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