Как отобразить результат асинхронной функции

Я пытаюсь изучить React Native и базу данных Google. Мне удалось успешно создать пользователей, и у меня есть функция, которая успешно получает всех пользователей и записывает их имя пользователя в консоль. Однако у меня много проблем с поиском способа отобразить список имен пользователей на экране.

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

У меня есть это в моем файле методов firebase:

firebase/index.js

export const getAllUsers = () => firebase.firestore().collection('users').get(); 

И у меня есть этот экран, который в настоящее время вызывает функцию, которая получает все имена пользователей и записывает их в консоль. Я хочу иметь доступ к имени пользователя (и, возможно, даже к роли и возрасту) и создавать с ними что-то вроде элементов Text></Text. Итак, что-то вроде Text>users.userName>/Text>.

CharacterSelectScreen.js

import React, {useState} from 'react'
import { StyleSheet, Text, View, TouchableOpacity, TextInput, Button, Picker, ScrollView} from 'react-native'
import { NavigationContainer, useNavigation, useRoute } from '@react-navigation/native';
import * as firebase from '../../firebase'; 

const CharacterSelectScreen = ({navigation, route}) => {
    
       React.useEffect(()=>{
        console.info('Select Screen loaded.')
        userFetch();
      },[])

    const userFetch = async () => {
        try{
            const users = await firebase.getAllUsers();
            // users is a QuerySnapshot
            if (users.size > 0) {
                users.forEach(doc => {
                    userList.push(doc.data().userName);
                })
            }

        } catch (error) {
            console.info(error);
        }
        userList.forEach(item => console.info(item)); //usreList contains all of the names. This works.
        return userList;
    }


    return (
        <View style = {{ flex: 1, justifyContent: 'flex-start' }}>
            <Text>Select a character:</Text>
      </View>
    )
}


export default CharacterSelectScreen;

Я не знаю, как мне взять результат асинхронной функции userFetch и присвоить его полезному массиву, который я мог бы просмотреть. Может быть, что-то вроде этого:

var names = userFetch();
        return (
            <ul>
                {names.map(function(name, index){
                    return <li key = { index }>{name}</li>;
                  })}
            </ul>
        )
    }
});

Но, возможно, это даже не лучший подход. Если это не так, я готов учиться/изменять все, что мне просто нужно, в правильном направлении.

Я думаю, вы, вероятно, хотите, чтобы отдельный компонент получал пользователя и роль и т. д.

mrlindsey 10.12.2020 23:36
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
1
1 427
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если я правильно понял ваш вопрос, вам может потребоваться следующий шаблон:

const Foo = () => {
  const [results, setResults] = useState([]);

  useEffect(() => {
    const asyncFunc = async () => {
      const response = await fetch(ENDPOINT);
      const { results } = await response.json();

      setResults(results);
    }

    asyncFunc();
  }, [deps];  

  return (
    <>
      {results.map((result) => <Bar result = {result}/>)}
    </>
  )
}

Я, наконец, получил это, и я не использовал ваш код точно, но это очень помогло! Спасибо.

Cole Perry 15.12.2020 15:24
Ответ принят как подходящий

Возможным решением является использование алгоритма сравнения React для автоматического отображения желаемой информации о пользователе (путем интерполяции в вашем ответе). Это можно сделать, создав экземпляр переменной, которая оценивается на основе условного выражения с использованием текущего состояния вашего компонента. Если это условие оценивается как true он возвращает дочерний компонент для каждого пользователя в вашем извлеченном ответе (UserIndexItem).

Что касается соглашения, я рекомендую абстрагировать ваш #userFetch через компонент-контейнер, который передает метод как свойство вашему CharacterSelectScreen, чтобы вы могли лучше высушить свой код. Пример: использование #useEffect, которое устанавливает состояние в возвращаемое значение #userFetch. Это соответствует разделению проблем в вашем коде.

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

См. ниже фрагмент.

import React, { useState } from 'react'
import { StyleSheet, Text, View, TouchableOpacity, TextInput, Button, Picker, ScrollView } from 'react-native'
import { NavigationContainer, useNavigation, useRoute } from '@react-navigation/native';
import * as firebase from '../../firebase';

import UserIndexItem from '...';
// The above import is pseudocode on how you would import a component

const CharacterSelectScreen = ({ navigation, route }) => {
  const [ users, updateUsers ] = useState([])

  useEffect(() => {
    console.info('Select Screen loaded.')
    updateUsers( userFetch() )
  }, [])
  // Great use of #useEffect to simulate React's #componentDidMount

  const userFetch = async () => {
    
    try {
      let userCollection = []
      const fetchedUsers = await firebase.getAllUsers();
      // unless #getAllUsers automatically returns JSON then we need to ensure its formatted for parseable data
      const usersJSON = await fetchedUsers.json()

      usersJSON.forEach( userObject => {
        userCollection.push( userObject.data() );
      })
      return userCollection

    } catch (error) {
      console.info(error);
    }
  }

  // The below conditional allows us to safely apply 
   // logic on undefined receivers because of the ternary 
    // condition. On re-render of our component, this function will 
     // return child components instead.

  const userIndex = users.length != 0 ? 
      users.map( user => {
        // where we can deconstruct the user object for DRYer code
        return (
          <UserIndexItem key = {user.id} username = {user.username} />
        )
      })
    : <span> No Users Yet!</span>
  

  return (
    <View style = {{ flex: 1, justifyContent: 'flex-start' }}>
      <ul>
        { userIndex }
      </ul>
    </View>
  )
}


export default CharacterSelectScreen;

Соглашение React о сопоставлении компонентов из массива см.: https://reactjs.org/docs/lists-and-keys.html

Теперь вы можете делать то, что @AsherLim делал в своем блоке возврата с тегами-призраками, но любые встроенные функции в функции рендеринга компонента вызовут непреднамеренный повторный рендеринг вашего компонента каждый раз, когда происходит изменение его родительского компонента (это может привести к проблемы с производительностью). Ссылку на это утверждение можно найти здесь: https://medium.com/@Osterberg/react-component-renders-too-often-2917daabcf5

Я сейчас далеко от своего компьютера, но это похоже именно то, что я искал. Большое спасибо за этот ответ. Я дам вам знать, как это происходит, когда я вернусь к своему компьютеру.

Cole Perry 11.12.2020 03:16

Как правильно использовать «импорт UserIndexItem из '...';» ? Эта строка выдает ошибку, потому что не может найти "...", и, честно говоря, я понятия не имею, как это исправить. Кроме того, у моих пользователей нет идентификаторов, поэтому должно ли это быть <UserIndexItem username = {user.username} />?

Cole Perry 11.12.2020 19:58

Я также хочу уточнить, что вы говорите, что может быть лучшим подходом создать другой компонент, который получает список пользователей, а затем передает этих пользователей второму компоненту?

Cole Perry 11.12.2020 21:32

Эй, @ColePerry, import UserIndexItem from '...' был псевдокодом, рекомендовавшим, как вы должны импортировать новый компонент для конкретных пользователей. Если вы создаете компонент с таким именем, вы должны иметь возможность деконструировать любые свойства, которые вы ему передаете, чтобы отобразить желаемые результаты.

Dan der Mensch 11.12.2020 21:35

Что касается вашего второго вопроса, я рекомендовал подход, похожий на редукцию, к абстрагированию кода, но проще всего было бы создать папку API utils, содержащую функцию, которая делает то, что вы пытаетесь выполнить в своем компоненте. Не говорю, что вы должны, но я лично считаю, что лучше абстрагировать асинхронные методы, которые вызывают API, от моих компонентов.

Dan der Mensch 11.12.2020 21:41

Хорошая привычка кодирования, которую я обнаружил, заключается в том, что если другому разработчику нужно будет что-то добавить в мою кодовую базу, я не хочу заставлять их визуально анализировать мой код. Они могут видеть, что CharacterSelectScreen делает только то, что описывает, с вызовами методов, уже созданных в другом месте. Имеет ли это смысл?

Dan der Mensch 11.12.2020 21:45

@ColePerry я ответил на твои вопросы? Если нет, пожалуйста, дайте мне знать, что еще я могу сделать.

Dan der Mensch 13.12.2020 02:21

Кажется, я понимаю, что вы сейчас отвечаете. Хотя я боюсь, что мне может не хватать понимания React Native, необходимого для использования этой информации для решения моей проблемы. Хотя я нахожусь в процессе разделения асинхронной функции, которая переводила всех пользователей в другой компонент (что, как я думал, было тем, что вы рекомендовали).

Cole Perry 14.12.2020 02:38

Сделал кучу домашних заданий по этому предмету, и я, наконец, понял. Большое спасибо! Но не могли бы вы немного объяснить функцию const userIndex? Я не использовал это именно потому, что не совсем понял это, но похоже, что это было бы удобно.

Cole Perry 15.12.2020 15:26

Ша! В тот момент, когда вы визуализируете свой компонент, ваш CharacterSelectScreen не заполняется пользователями сразу (до получения). Функция выполняет условную визуализацию примера компонента: CharacterIndexItem на основе состояния, являющегося пустым массивом или нет. Почему это работает: при начальном рендеринге ничего нет и состояние пусто, когда ваш метод жизненного цикла #useEffect видит, что компонент смонтирован в DOM, он обновляет состояние с пользователями, вызывая ваш метод.

Dan der Mensch 16.12.2020 05:05

И всякий раз, когда React обнаруживает изменение в состоянии (важно использовать алгоритм сравнения Google React), он повторно отображает предыдущее состояние и, следовательно, заставляет #userIndex оценивать истинное состояние!

Dan der Mensch 16.12.2020 05:05

Большое спасибо за это объяснение. Это на самом деле проясняет пару вещей.

Cole Perry 16.12.2020 19:10

Конечно! Я настоятельно рекомендую проверить: medium.com/@abdellani/… и felixgerschau.com/react-rerender-components для понимания алгоритма сравнения React. И, наконец, проверьте reactjs.org/docs/lists-and-keys.html для рендеринга списков элементов.

Dan der Mensch 16.12.2020 19:16

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