React Native 中的无限循环,因为 useState



我有 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

主要问题是,如果使用useState保存我的{user},我会创建一个无限循环。

2/我试图通过不使用 useState 保存它来避免这种情况,并直接在视图中返回值 response.data.username ,但它不起作用。我知道我不能返回承诺的值,这就是我使用 useState 的原因。

我是一个乞丐{可能不是一个好乞丐},我试图了解 React 和 axios 的工作原理,但我已经到了极限,无法猜测解决方案......

谢谢你的帮助,如果有人敢^^

无限循环是因为在返回的 JSX 中调用getUserName

您可以尝试获取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;

在此示例中,我将usermessages合并到allMessages中,但您可以根据需要进行更改。

这里的核心问题是每个渲染都调用getUserName,而 反过来设置状态,从而触发另一个渲染。

render => getUserName => setUser => render => getUserName => setUser => render

一种解决方案是创建一个单独的用户名组件来处理名称解析:

const DisplayName = ({userid}) => {
const [displayName, setDisplayName] = useState();
useEffect(() => {
// resolve username and call setDisplayName
}, [userid]);
return (
<span>{displayName}</span>
);
}

然后你可以传递作者 ID 并让它处理它:

return (
<View style={styles.container}>
{allMessages.map(message => (
<Text>
<DisplayName userid={message.author.id} />: {message.content}
</Text>
))
}
</View>
)

您仍然会遇到为聊天中的每条消息重新获取用户名的问题,但它解决了无限循环问题。

为了解决重复的用户名获取问题,您可以将它们缓存在组件之外的某个位置(商店,一个 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>
))
}

谢谢你的回答。我都试过了,每个都解决了一些问题,但不是全部。

我可能更具体一点:我正在寻找更简单的代码,即使我必须重新思考我的方法的逻辑。

这是我想做的:

1/访问带有 axios 的 API,以获取发布在某个区域的所有消息的数据。这些数据为我提供了消息的内容、我要显示的女巫以及用户的 Id。

2/我想使用用户的 Id. 对 API 进行第二次调用,以便这次获取用户的名称,将其显示在消息内容之前。

我选择的主要方法: A/我可以将消息内容和用户名保存在不同的数组中,并并行映射它们以显示数据。 B/我只能调用一个 API 来获取消息的所有数据,将它们存储在一个数组中,然后映射数组以显示消息的内容,并在那时对 API 进行第二次调用以从他的 ID 中检索用户的名称。这就是我尝试过的解决方案。

是的,我是一个菜鸟,但我认为我想做的是基本的,不需要那么多代码行。但也许我错了^^

相关内容

最新更新