React中Promise处于Pending状态导致无法创建li元素的问题及相关技术疑问
Let's break down your issues one by one and fix your code step by step:
1. Why your async/await function returns a Promise instead of the result
Async functions in JavaScript always return a Promise, no matter what value you return inside them. Even if you return a plain string or array, it gets wrapped in a resolved Promise automatically—that's just how the async/await syntax works under the hood.
On top of that, you're using an async function directly as your useEffect callback. useEffect expects the callback to either return nothing or a cleanup function. When you pass an async function, it returns a Promise, which React can't interpret as a cleanup function—this is a hidden bug in your current code.
2. Fixing the data fetching logic to populate chat correctly
Your main problem right now is that you're setting chat to res.json(), which is a Promise (not the actual array of messages). Plus, you aren't waiting for res.json() to finish parsing the response. Here's how to fix this useEffect:
useEffect(() => { let mounted = true; const fetchChatHistory = async () => { try { const response = await fetch("http://localhost:4000/main_chat"); // Add a check for successful HTTP response if (!response.ok) throw new Error(`Failed to fetch: ${response.status}`); if (mounted) { const chatData = await response.json(); // Wait for JSON parsing to complete setChat(chatData); // Now setChat gets the actual array, not a Promise } } catch (error) { console.error("Error loading chat history:", error); } }; fetchChatHistory(); return () => { mounted = false; }; }, []);
Key changes here:
- We define an async function inside
useEffectinstead of making theuseEffectcallback itself async. This avoids returning a Promise to React. - We
await response.json()because parsing the response body as JSON is an asynchronous operation. - Added error handling with
try/catchto catch network issues or invalid responses—critical for real-world apps.
3. Rendering the chat messages (fixing the <li> creation)
Right now, you're just logging chat in the <ul>. Once chat is an array, you can map over it to create <li> elements. Since your initial chat state is an empty array, you don't need extra guard clauses, but here's how to render the messages:
<ul> {chat.map((message, index) => ( <li key={index}>{message}</li> ))} </ul>
Note: Using index as a key works fine here if your messages don't have unique IDs and their order doesn't change. For production, use a unique identifier (like a message ID from your backend) if available.
4. Answers to your specific questions
- How to access the Promise object? You don't need to access the Promise directly here—you want its resolved value (the array of messages). If you ever end up with a Promise in state (which you should avoid), you'd use
.then()orawaitto get its value, but in React, we always wait for Promises to resolve before updating state. - How to map over a Promise? You can't map over a Promise directly. You have to wait for it to resolve to an array first, then map over that array. That's exactly what our fixed
useEffectdoes: once we have the parsed array, we put it in state, and then we can safely map over it in the render.
Full corrected code
import { io } from "socket.io-client"; import { useState, useEffect } from "react"; function App() { const [text, setText] = useState(""); const [socket, setSocket] = useState(); const [chat, setChat] = useState([]); // Initialize socket connection useEffect(() => { const newSocket = io("http://localhost:4000"); setSocket(newSocket); return () => { newSocket.close(); }; }, []); // Fetch previous chat history for new users useEffect(() => { let mounted = true; const fetchChatHistory = async () => { try { const response = await fetch("http://localhost:4000/main_chat"); if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`); const chatData = await response.json(); if (mounted) setChat(chatData); } catch (error) { console.error("Failed to load chat history:", error); } }; fetchChatHistory(); return () => { mounted = false; }; }, []); // Handle incoming socket messages useEffect(() => { if (!socket) return; socket.on("sentFromServer", (message) => { setChat(prevChat => [...prevChat, message]); }); return () => { socket.off("sentFromServer"); }; }, [socket]); const handleClick = () => { if (socket && text.trim()) { socket.emit("sentFromClient", text); setText(""); // Clear input after sending } }; return ( <div> <ul> {chat.map((message, index) => ( <li key={index}>{message}</li> ))} </ul> <input value={text} onChange={(e) => setText(e.target.value)} placeholder="Type your message..." /> <button onClick={handleClick}>Send</button> </div> ); } export default App;
内容的提问来源于stack exchange,提问作者imback0526

