【问题标题】:Infinite loop in React Native because of a useState由于 useState,React Native 中的无限循环
【发布时间】:2021-01-06 23:41:09
【问题描述】:

我有 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 是如何工作的,但我已经到了极限,无法猜测解决方案...

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

【问题讨论】:

    标签: reactjs react-native infinite-loop use-state


    【解决方案1】:

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

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

    这是我想做的:

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

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

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

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

    【讨论】:

      【解决方案2】:

      这里的核心问题是每次渲染都会调用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>
      )
      

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

      要解决重复的用户名获取问题,您可以将它们缓存在组件外部的某个位置(商店、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】:

        无限循环是因为在返回的 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,但您可以随意更改。

        【讨论】:

          猜你喜欢
          • 2020-02-21
          • 1970-01-01
          • 2021-05-17
          • 2023-02-05
          • 2019-09-13
          • 1970-01-01
          • 2022-09-30
          • 2021-10-05
          • 2018-05-08
          相关资源
          最近更新 更多