You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+TypeScript中显示函数返回字符串遇类型错误求助

解决React中Firebase异步获取用户名并渲染的问题

问题根源

直接在JSX中调用async函数会返回Promise,而React无法将Promise作为ReactNode渲染,因此出现Type 'Promise<string>' is not assignable to type 'ReactNode'错误。另外你的getUserName函数路径有误——应该从users集合而非tasks集合获取用户数据,因为你是通过userID查询用户名。

方案一:预加载所有用户名(推荐)

一次性获取所有任务对应的用户名并存储到状态中,避免重复请求,提升性能:

  1. 添加状态存储用户名映射
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组件处理异步请求

将每个任务项拆分为独立组件,单独处理自身的用户名加载状态:

  1. 创建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;
  1. 在父组件中使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 01:54:55