【问题标题】:setState in nested async function - React Hooks嵌套异步函数中的 setState - React Hooks
【发布时间】:2020-05-10 09:25:55
【问题描述】:

如何构建一个异步获取一些数据然后使用该数据获取更多异步数据的函数?

我正在使用 Dexie.js(indexedDB 包装器)来存储有关直接消息的数据。我在对象中存储的一件事是我将要向其发送消息的用户 ID。为了构建更好的 UI,我还获取了有关该用户的一些信息,例如存储在远程 rdbms 上的个人资料图片、用户名和显示名称。构建完整的链接组件需要来自两个数据库(本地 indexedDB 和远程 rdbms)的数据。

我的解决方案返回一个空数组。在 Google Chrome 中记录它时正在计算它,我确实看到了我的数据。然而,因为这不是在渲染时计算的,所以数组总是空的,因此我不能迭代它来构建一个组件。

const [conversations, setConversations] = useState<IConversation[]>()
const [receivers, setReceivers] = useState<Profile[]>()
useEffect(() => {
    messagesDatabase.conversations.toArray().then(result => {
      setConversations(result)
    })
  }, [])

  useEffect(() => {
    if (conversations) {
      const getReceivers = async () => {
        let receivers: Profile[] = []
        await conversations.forEach(async (element) => {
          const receiver = await getProfileById(element.conversationWith, token)
          // the above await is a javascript fetch call to my backend that returns json about the user values I mentioned
          receivers.push(receiver)
        })
        return receivers
      }
      getReceivers().then(receivers => {
        setReceivers(receivers)
      })
    }
  }, [conversations])
  /*
    The below log logs an array with a length of 0; receivers.length -> 0
    but when clicking the log in Chrome I see:
    [
     0: {
       avatarURL: "https://lh3.googleusercontent.com/..."
       displayName: "Cool guy"
       userId: "1234"
       username: "cool_guy"
     }
     1: ...
    ]

  */
  console.log(receivers) 

我的计划是使用 map 遍历这个数组

{
  receivers && conversations
  ? receivers.map((element, index) => {
    return  <ChatLink 
              path={conversations[index].path}
              lastMessage={conversations[index].last_message}
              displayName={element.displayName}
              username={element.username}
              avatarURL={element.avatarURL}
              key={index}
            />
    })
  : null
}

我怎样才能写这个不返回一个空数组?
这是一个与我正在经历的事情相关的 SO 问题here

【问题讨论】:

    标签: javascript reactjs indexeddb


    【解决方案1】:

    当您尝试执行以下操作时,我相信您的问题与您的第二个 useEffect 挂钩有关:

    const getReceivers = async () => {
      let receivers: Profile[] = []
      await conversations.forEach(async (element) => {
        const receiver = await getProfileById(element.conversationWith, token)
          receivers.push(receiver)
        })
        return receivers
       }
       getReceivers().then(receivers => {
         setReceivers(receivers)
       })
    }
    

    很遗憾,这不起作用,因为async/await 不适用于forEach。您需要使用for...ofPromise.all() 正确地遍历所有conversations,调用您的API,然后在完成后设置state

    这是使用Promise.all()的解决方案:

    function App() {
      const [conversations, setConversations] = useState<IConversation[]>([]);
      const [receivers, setReceivers] = useState<Profile[]>([]);
    
      useEffect(() => {
        messagesDatabase.conversations.toArray().then(result => {
          setConversations(result);
        });
      }, []);
    
      useEffect(() => {
        if (conversations.length === 0) {
          return;
        }
        async function getReceivers() {
          const receivers: Profile[] = await Promise.all(
            conversations.map(conversation =>
              getProfileById(element.conversationWith, token)
            )
          );
          setReceivers(receivers);
        }
        getReceivers()
      }, [conversations]);
    
      // NOTE: You don't have to do the `receivers && conversations`
      // check, and since both are arrays, you should check whether
      // `receivers.length !== 0` and `conversations.length !== 0`
      // if you want to render something conditionally, but since your
      // initial `receivers` state is an empty array, you could just 
      // render that instead and you won't be seeing anything until 
      // that array is populated with some data after all fetching is
      // done, however, for a better UX, you should probably indicate
      // that things are loading and show something rather than returning
      // an empty array or null
      return receivers.map((receiver, idx) => <ChatLink />)
    
      // or, alternatively
      return receivers.length !== 0 ? (
        receivers.map((receiver, idx) => <ChatLink />)
      ) : (
        <p>Loading...</p>
      );
    }
    

    或者,使用for...of,您可以执行以下操作:

    function App() {
      const [conversations, setConversations] = useState<IConversation[]>([]);
      const [receivers, setReceivers] = useState<Profile[]>([]);
    
      useEffect(() => {
        messagesDatabase.conversations.toArray().then(result => {
          setConversations(result);
        });
      }, []);
    
      useEffect(() => {
        if (conversations.length === 0) {
          return;
        }
        async function getReceivers() {
          let receivers: Profile[] = [];
          const profiles = conversations.map(conversation =>
            getProfileById(conversation.conversationWith, token)
          );
          for (const profile of profiles) {
            const receiver = await profile;
            receivers.push(receiver);
          }
          return receivers;
        }
        getReceivers().then(receivers => {
          setReceivers(receivers);
        });
      }, [conversations]);
    
      return receivers.map((receiver, idx) => <ChatLink />);
    }
    

    【讨论】:

      【解决方案2】:

      我认为它正在发生,因为 getReceivers() 函数是异步的。它等待响应,同时您的状态呈现为空数组。

      您可以在收到响应之前显示微调器。 喜欢

      const[isLoading,setLoading]= useState(true)
          useEffect(()=>{
               getReceivers().then(()=>{setLoading(false)}).catch(..)
              } )
        return  {isLoading ? <spinner/> : <yourdata/>}
      

      【讨论】:

        【解决方案3】:

        请将接收者初始值设置为数组

        const [receivers, setReceivers] = useState<Profile[]>([])
        

        foreach 也不会像你期望的那样等待使用 for 循环而不是 foreach

        我不确定它是否可以解决您的问题 但它可以帮助你解决你的错误

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-10-12
          • 1970-01-01
          • 1970-01-01
          • 2020-01-15
          • 1970-01-01
          • 2014-10-05
          • 2019-10-02
          相关资源
          最近更新 更多