Я пытаюсь изучить React Native и базу данных Google. Мне удалось успешно создать пользователей, и у меня есть функция, которая успешно получает всех пользователей и записывает их имя пользователя в консоль. Однако у меня много проблем с поиском способа отобразить список имен пользователей на экране.
Я думаю, что большая часть моих проблем связана с тем, что я использую функциональный компонент вместо компонента класса, который я вижу в похожих вопросах, и до сих пор мне было трудно переводить между ними.
У меня есть это в моем файле методов firebase:
export const getAllUsers = () => firebase.firestore().collection('users').get();
И у меня есть этот экран, который в настоящее время вызывает функцию, которая получает все имена пользователей и записывает их в консоль. Я хочу иметь доступ к имени пользователя (и, возможно, даже к роли и возрасту) и создавать с ними что-то вроде элементов Text></Text. Итак, что-то вроде Text>users.userName>/Text>.
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>
)
}
});
Но, возможно, это даже не лучший подход. Если это не так, я готов учиться/изменять все, что мне просто нужно, в правильном направлении.
Если я правильно понял ваш вопрос, вам может потребоваться следующий шаблон:
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}/>)}
</>
)
}
Я, наконец, получил это, и я не использовал ваш код точно, но это очень помогло! Спасибо.
Возможным решением является использование алгоритма сравнения 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
Я сейчас далеко от своего компьютера, но это похоже именно то, что я искал. Большое спасибо за этот ответ. Я дам вам знать, как это происходит, когда я вернусь к своему компьютеру.
Как правильно использовать «импорт UserIndexItem из '...';» ? Эта строка выдает ошибку, потому что не может найти "...", и, честно говоря, я понятия не имею, как это исправить. Кроме того, у моих пользователей нет идентификаторов, поэтому должно ли это быть <UserIndexItem username = {user.username} />?
Я также хочу уточнить, что вы говорите, что может быть лучшим подходом создать другой компонент, который получает список пользователей, а затем передает этих пользователей второму компоненту?
Эй, @ColePerry, import UserIndexItem from '...'
был псевдокодом, рекомендовавшим, как вы должны импортировать новый компонент для конкретных пользователей. Если вы создаете компонент с таким именем, вы должны иметь возможность деконструировать любые свойства, которые вы ему передаете, чтобы отобразить желаемые результаты.
Что касается вашего второго вопроса, я рекомендовал подход, похожий на редукцию, к абстрагированию кода, но проще всего было бы создать папку API utils, содержащую функцию, которая делает то, что вы пытаетесь выполнить в своем компоненте. Не говорю, что вы должны, но я лично считаю, что лучше абстрагировать асинхронные методы, которые вызывают API, от моих компонентов.
Хорошая привычка кодирования, которую я обнаружил, заключается в том, что если другому разработчику нужно будет что-то добавить в мою кодовую базу, я не хочу заставлять их визуально анализировать мой код. Они могут видеть, что CharacterSelectScreen
делает только то, что описывает, с вызовами методов, уже созданных в другом месте. Имеет ли это смысл?
@ColePerry я ответил на твои вопросы? Если нет, пожалуйста, дайте мне знать, что еще я могу сделать.
Кажется, я понимаю, что вы сейчас отвечаете. Хотя я боюсь, что мне может не хватать понимания React Native, необходимого для использования этой информации для решения моей проблемы. Хотя я нахожусь в процессе разделения асинхронной функции, которая переводила всех пользователей в другой компонент (что, как я думал, было тем, что вы рекомендовали).
Сделал кучу домашних заданий по этому предмету, и я, наконец, понял. Большое спасибо! Но не могли бы вы немного объяснить функцию const userIndex? Я не использовал это именно потому, что не совсем понял это, но похоже, что это было бы удобно.
Ша! В тот момент, когда вы визуализируете свой компонент, ваш CharacterSelectScreen
не заполняется пользователями сразу (до получения). Функция выполняет условную визуализацию примера компонента: CharacterIndexItem
на основе состояния, являющегося пустым массивом или нет. Почему это работает: при начальном рендеринге ничего нет и состояние пусто, когда ваш метод жизненного цикла #useEffect
видит, что компонент смонтирован в DOM, он обновляет состояние с пользователями, вызывая ваш метод.
И всякий раз, когда React обнаруживает изменение в состоянии (важно использовать алгоритм сравнения Google React), он повторно отображает предыдущее состояние и, следовательно, заставляет #userIndex
оценивать истинное состояние!
Большое спасибо за это объяснение. Это на самом деле проясняет пару вещей.
Конечно! Я настоятельно рекомендую проверить: medium.com/@abdellani/… и felixgerschau.com/react-rerender-components для понимания алгоритма сравнения React. И, наконец, проверьте reactjs.org/docs/lists-and-keys.html для рендеринга списков элементов.
Я думаю, вы, вероятно, хотите, чтобы отдельный компонент получал пользователя и роль и т. д.