#reactjs #react-native #infinite-loop #use-state
#reactjs #react-native #бесконечный цикл #use-state
Вопрос:
У меня есть 1 / бесконечный цикл, использующий useState для сохранения данных из запроса axios. Я хочу ввести значение в представление во время сопоставления :
import { StyleSheet, Text, View, TextInput, FlatList, Button, ScrollView } from 'react-native';
import styles from './styles'
import React, { useEffect, useState, Component } from 'react';
import App, { currentCityCode } from './app'
import axios from 'axios';
import { Dropdown } from 'react-native-material-dropdown';
function Chat(props) {
useEffect(() => {
getMessage()
}, []
);
const messApiRest = "https://..."
const [lastMessage, setLastMessage] = useState("")
const [user, setUser] = useState("")
const [categoryMessage, setcategoryMessage] = useState("")
const [idUser, setIdUser] = useState("")
const [allMessages, setAllMessages] = useState([])
const [messageOneByOne, seMessageOneByOne] = useState([])
function getMessage() {
axios.get(messApiRest "/api/messages",
{
headers: {
'Authorization': "Bearer " props.token,
},
params: {
'citycode': props.cityCode
},
})
.then(response => {
setAllMessages(response.data.results)
},
)
}
function getUserName(toto) {
axios.get(messApiRest "/api/users/" toto,
{
headers: {
'Authorization': "Bearer " props.token,
}
}
)
.then(response => {
console.log(response.data.username)
setUser(response.data.username)
})
}
return (
<View style={styles.container}>
{allMessages.map(function (message) {
getUserName(message.author.id)
return (
<Text>{user}: {message.content}</Text>
)
})}
</View>
)
}
export default Chat
Основная проблема заключается в том, что если сохранить мой {user}, используя useState, я создаю бесконечный цикл.
2 / Я пытался избежать этого, не сохраняя его с помощью useState и напрямую возвращая значение response.data.username в представлении, но это не работает. Я знаю, что не могу вернуть значение promise , поэтому вместо этого я использую useState .
Я новичок {вероятно, не очень хороший}, я пытаюсь понять, как работают React и axios, но я на пределе и не могу угадать решение…
Спасибо за вашу помощь, если кто-то осмелится ^^
Ответ №1:
Бесконечный цикл возникает из-за вызова getUserName
возвращаемого JSX.
Вы можете попробовать выборку пользователей и сообщений внутри useEffect
и обновлять состояние только тогда, когда они оба извлекаются:
function Chat(props) {
const messApiRest = "https://...";
const [lastMessage, setLastMessage] = useState("");
const [user, setUser] = useState("");
const [categoryMessage, setcategoryMessage] = useState("");
const [idUser, setIdUser] = useState("");
const [allMessages, setAllMessages] = useState([]);
const [messageOneByOne, seMessageOneByOne] = useState([]);
useEffect(() => {
getMessage()
.then((res) => {
return res.data.results;
})
.then((msgs) => {
const userPromises = msgs.map((m) => getUserName(m.author.id));
Promise.all(userPromises).then((users) => {
setAllMessages(
msgs.map((msg, index) => ({ ...msg, user: users[index] }))
);
});
});
}, []);
function getMessage() {
return axios.get(messApiRest "/api/messages", {
headers: {
Authorization: "Bearer " props.token,
},
params: {
citycode: props.cityCode,
},
});
}
function getUserName(toto) {
return axios.get(messApiRest "/api/users/" toto, {
headers: {
Authorization: "Bearer " props.token,
},
});
}
return (
<View style={styles.container}>
{allMessages.map(function (message) {
return (
<Text>
{message.user}: {message.content}
</Text>
);
})}
</View>
);
}
export default Chat;
В этом примере я объединил user
и messages
в allMessages
, но вы можете изменить его по своему усмотрению.
Ответ №2:
Основная проблема здесь заключается в том, что каждый вызов render getUserName
, который, в свою очередь, устанавливает состояние, которое запускает другой рендеринг.
render => getUserName => setUser => render => getUserName => setUser => render
Одним из решений было бы создание отдельного компонента username, который обрабатывает разрешение имен:
const DisplayName = ({userid}) => {
const [displayName, setDisplayName] = useState();
useEffect(() => {
// resolve username and call setDisplayName
}, [userid]);
return (
<span>{displayName}</span>
);
}
Затем вы можете передать идентификатор автора и позволить ему обработать его:
return (
<View style={styles.container}>
{allMessages.map(message => (
<Text>
<DisplayName userid={message.author.id} />: {message.content}
</Text>
))
}
</View>
)
У вас все еще есть проблема с повторной выборкой имени пользователя для каждого сообщения в чате, но это решает проблему бесконечного цикла.
Чтобы решить повторяющуюся выборку имени пользователя, вы можете либо кэшировать их где-то за пределами компонента (хранилище, a la redux), либо переместить его обратно в родительский компонент и разрешить все имена пользователей заранее:
const [usernames, setUsernames] = useState({});
const resolveUsernames = useCallback(async authorIds => {
// create an array of 'getUserName' promises and wait for them to all come back
const usernames = await Promise.all(authorIds.map(id => getUserName(id)));
// map the names back to the corresponding ids, e.g. { 123: 'alice', 456: 'bob'}
// Promise.all results come back in the same order as the input promises, so you can associate them by index.
const idMap = authorIds.reduce((ids, id, index) => ({
...ids,
[id]: usernames[index]
}), {});
// store the id-to-name mapping in state:
setUsernames(idMap);
}, [authorIds])
function getMessage() {
axios.get(messApiRest "/api/messages",
{
headers: { 'Authorization': "Bearer " props.token },
params: { 'citycode': props.cityCode },
})
.then(response => {
const results = response.data.results;
// this is just a shorthand way of getting an array of distinct ids
const authorIds = [...new Set(results.map(message => message.author.id))];
resolveUsernames(authorIds);
setAllMessages(response.data.results)
})
}
Установив это, вы можете искать имя пользователя для каждого сообщения из состояния компонента usernames
:
{allMessages.map(message => (
<Text>
{usernames[message.author.id]}: {message.content}
</Text>
))
}
Ответ №3:
Спасибо за ваш ответ. Я пробовал оба, каждый решал некоторые проблемы, но не все.
Я мог бы быть немного более конкретным: я ищу более простой код, даже если мне придется переосмыслить логику моего подхода.
Вот что я хочу сделать :
1 / Доступ к API с помощью axios для получения данных всех сообщений, размещенных в определенной области. Эти данные дают мне содержимое сообщения, которое я хочу отобразить, и идентификатор пользователя.
2 / Я хочу использовать идентификатор пользователя для выполнения второго вызова API, чтобы на этот раз получить имя пользователя, чтобы отобразить его перед содержимым сообщения.
Основные подходы, которые я выбрал: A / I может сохранять как содержимое сообщений, так и имя пользователя в разных массивах и сопоставлять их параллельно для отображения данных. B / I может вызывать API только для получения всех данных сообщений, складирования их в массив, затем сопоставления массива для отображения содержимого сообщения и выполнения в это время второго вызова API для извлечения имени пользователя из его идентификатора. Это решение, которое я пробовал.
Да, я новичок, но я думаю, что то, что я хочу сделать, является базовым и не требует такого количества строк кода. Но, возможно, я ошибаюсь ^^