React中Uncaught TypeError: projects.map不是函数问题排查
问题解决:React组件首次渲染时
projects.map报错 问题根源
你的代码核心问题是初始state类型错误:
const [projects, setProjects] = useState('');
首次渲染时projects是空字符串(而非数组),字符串并没有map方法,直接调用projects.map就会抛出Uncaught TypeError: projects.map is not a function错误。同时Array.isArray(projects)返回false,所以Array.isArray(projects)&&projects.length结果也为false。
等到useEffect里的异步请求完成后,setProjects把projects更新为数组,第二次渲染时就能正常执行map逻辑了。
修复方案
1. 修正初始state为数组
把初始值从空字符串改成空数组,这样首次渲染时projects是数组类型,即便为空也能安全调用map:
const [projects, setProjects] = useState([]);
2. (可选)添加加载状态优化体验
如果数据加载耗时较长,可添加加载状态避免页面空白:
const [projects, setProjects] = useState([]); const [isLoading, setIsLoading] = useState(true); // 新增加载状态 useEffect(()=>{ const getProjects = async () => { const data = await getDocs(projectRef) setProjects(data.docs.map((doc) => ({ ...doc.data(), id: doc.id }))) setIsLoading(false); // 数据加载完成后关闭加载状态 } getProjects() },[]); // 渲染时先判断加载状态 if (isLoading) { return <div>加载中...</div>; }
3. (可选)添加类型检查(双重保障)
如果担心projects被意外修改为非数组类型,可在调用map前做类型校验:
const projec = Array.isArray(projects) ? projects.map(proj => { // 原map逻辑 }) : [];
完整修复后的代码
import { useEffect, useState } from 'react'; import {useDispatch } from 'react-redux'; import { deleteProject } from '../../store/projectSlice'; import { db } from '../../config/firebaseConfig'; import { getDocs , collection } from 'firebase/firestore'; const ProjectSummary = () => { // 修正初始state为数组 const [projects, setProjects] = useState([]); const [isLoading, setIsLoading] = useState(true); const projectRef = collection(db, 'projects') const dispatch = useDispatch(); useEffect(()=>{ const getProjects = async () => { const data = await getDocs(projectRef) setProjects(data.docs.map((doc) => ({ ...doc.data(), id: doc.id }))) setIsLoading(false); } getProjects() },[]); const handleDelete = (id) => { dispatch(deleteProject(id)) } if (isLoading) { return <div>加载中...</div>; } const projec = projects.map(proj => { return ( <div className="card z-depth-o project-summary" key={proj.id}> <div className="card-content grey-text text-darken-3" > <span className="card-title"> {proj.title}</span> <p>{proj.content}</p> <p className="grey-text">less than a minute ago</p> <button onClick={() => handleDelete(proj.id)} >Delete Project </button> </div> </div> ) }) return ( <div>{ projec}</div> ) } export default ProjectSummary
内容的提问来源于stack exchange,提问作者erfan
相关产品推荐
相关产品推荐

