React+TypeScript中显示函数返回字符串遇类型错误求助
解决React中Firebase异步获取用户名并渲染的问题
问题根源
直接在JSX中调用async函数会返回Promise,而React无法将Promise作为ReactNode渲染,因此出现Type 'Promise<string>' is not assignable to type 'ReactNode'错误。另外你的getUserName函数路径有误——应该从users集合而非tasks集合获取用户数据,因为你是通过userID查询用户名。
方案一:预加载所有用户名(推荐)
一次性获取所有任务对应的用户名并存储到状态中,避免重复请求,提升性能:
- 添加状态存储用户名映射
import { useState, useEffect } from 'react'; // ...其他导入 const YourComponent = () => { const [taskList, setTaskList] = useState<Task[] | null>(null); const [userNames, setUserNames] = useState<Record<string, string>>({}); // 存储userID到用户名的映射 // 当taskList更新时批量获取用户名 useEffect(() => { const fetchUserNames = async () => { if (!taskList) return; // 去重userID,避免重复请求 const uniqueUserIDs = [...new Set(taskList.map(task => task.userID))]; const nameMap: Record<string, string> = {}; for (const userId of uniqueUserIDs) { try { const docRef = doc(db, `users/${userId}`); // 修正为用户集合路径 const userDoc = await getDoc(docRef); if (userDoc.exists()) { const userData = userDoc.data() as UserData; nameMap[userId] = userData.name; } else { nameMap[userId] = 'Unknown User'; } } catch (err) { nameMap[userId] = 'Failed to load'; } } setUserNames(nameMap); }; fetchUserNames(); }, [taskList]); // ...其他逻辑(比如获取taskList的代码) return ( <> <Container style={{ width: 'auto', border: 'solid 0.2rem', borderRadius: '2rem', marginTop: '2rem', padding: '3rem' }}> <h1>Tasks</h1> {taskList?.map((task: Task) => ( <Card key={task.id} style={{ width: '30rem', padding: '2rem' }}> <h3>{task.title}</h3> <p>{task.description}</p> <p>{task.timeCreated.toDate().toDateString()}</p> <p>Created By: {userNames[task.userID] || 'Loading...'}</p> <Button variant="danger" onClick={() => deleteTask(task, task.id)}>Delete Task</Button> </Card> ))} </Container> </> ); };
方案二:独立TaskItem组件处理异步请求
将每个任务项拆分为独立组件,单独处理自身的用户名加载状态:
- 创建TaskItem组件
import { useState, useEffect } from 'react'; import { Card, Button } from 'react-bootstrap'; // 根据你的组件库调整 import { doc, getDoc } from 'firebase/firestore'; import { db } from '../firebase-config'; // 导入你的Firebase配置 interface TaskItemProps { task: Task; deleteTask: (task: Task, id: string) => void; } const TaskItem = ({ task, deleteTask }: TaskItemProps) => { const [userName, setUserName] = useState<string>('Loading...'); useEffect(() => { const fetchUserName = async () => { try { const docRef = doc(db, `users/${task.userID}`); // 修正为用户集合路径 const userDoc = await getDoc(docRef); if (userDoc.exists()) { const userData = userDoc.data() as UserData; setUserName(userData.name); } else { setUserName('Unknown User'); } } catch (err) { setUserName('Failed to load'); } }; fetchUserName(); }, [task.userID]); return ( <Card style={{ width: '30rem', padding: '2rem' }}> <h3>{task.title}</h3> <p>{task.description}</p> <p>{task.timeCreated.toDate().toDateString()}</p> <p>Created By: {userName}</p> <Button variant="danger" onClick={() => deleteTask(task, task.id)}>Delete Task</Button> </Card> ); }; export default TaskItem;
- 在父组件中使用TaskItem
import TaskItem from './TaskItem'; // ...父组件逻辑 return ( <> <Container style={{ width: 'auto', border: 'solid 0.2rem', borderRadius: '2rem', marginTop: '2rem', padding: '3rem' }}> <h1>Tasks</h1> {taskList?.map((task: Task) => ( <TaskItem key={task.id} task={task} deleteTask={deleteTask} /> ))} </Container> </> );
内容的提问来源于stack exchange,提问作者Brad Goodall
相关产品推荐
相关产品推荐

