为何React中useEffect获取的状态需加?才能正常遍历?
?的原因 通过useEffect从API获取数据填充的React状态,为何必须在.map方法或length属性前添加?标记才能正常遍历?不添加会出现Cannot read properties of undefined (reading 'length')错误。
相关代码
1. 状态定义
const [userInProgressTasks, setUserInProgressTasks] = useState();
2. API数据请求
useEffect(() => { let isMounted = true; // 组件卸载时取消请求 const controller = new AbortController(); const getUserTasksInProgress = async () => { try { const response = await axiosPrivate.get( ApiConstants.GETALL_USER_IN_PROGRESS_TASKS_ENDPOINT(userObject?.id), { signal: controller.signal, } ); console.log(response?.data); isMounted && setUserInProgressTasks(response?.data); } catch (err) { console.err(err); // 令牌过期等错误时跳转登录页 navigate("/auth/login", { state: { from: location }, replace: true, }); } }; getUserTasksInProgress(); // useEffect清理函数:组件卸载时执行 return () => { isMounted = false; controller.abort(); }; }, []);
3. 状态遍历渲染
{userInProgressTasks?.length <= 0 ? ( <> <div className="NoTasks-Image-Container"> <img src={InProgressImage} /> </div> </> ) : ( <> {userInProgressTasks?.map((t) => ( <p>{t.taskId}</p> ))} </> )}
原因解析
这和?的“等待”功能无关,本质是React渲染顺序与状态初始值的问题:
初始状态未定义
你定义状态时没有设置初始值,userInProgressTasks默认是undefined。组件第一次渲染时,API请求还没返回结果,状态仍是undefined,直接访问undefined.length或undefined.map()就会触发“读取未定义属性”的错误。API请求是异步操作
useEffect中的API调用是异步的,组件不会等待请求完成再渲染。第一次渲染发生在请求pending阶段,此时状态还没被填充;只有当请求成功、调用setUserInProgressTasks更新状态后,组件才会重新渲染,此时状态才有有效数据。可选链操作符
?的作用userInProgressTasks?.length的逻辑是:如果userInProgressTasks存在(不是undefined或null),就读取它的length属性;如果不存在,直接返回undefined,不会抛出错误。这就规避了第一次渲染时的报错,直到状态被正确填充。
更优解决方案:设置初始空数组
替代可选链的方式是给状态设置合理的初始值(比如空数组):
const [userInProgressTasks, setUserInProgressTasks] = useState([]);
这样初始状态就是空数组,userInProgressTasks.length会是0,userInProgressTasks.map()也能正常执行(遍历空数组不会渲染内容),不需要加?也不会报错,逻辑更简洁清晰。
内容的提问来源于stack exchange,提问作者ahmed7am1d

