React应用读取substring时出现Undefined错误求助
解决React中TypeError: Cannot read properties of undefined (reading 'substring')问题
问题描述
在React(Next.js)应用中出现错误:TypeError: Cannot read properties of undefined (reading 'substring'),仅注释掉包含data.created_at.substring的<span>标签时应用可正常运行。尝试使用data ? data.created_at.substring(8,10) : ""的条件渲染方式后,该<span>行显示空白。
相关代码如下:
页面组件
const DocumentationDetail = () => { const router = useRouter(); const { user } = useAuth(); const id = router.asPath.substring(1); const { data } = useDocumentationGet(parseInt(id)); return ( <> <Head> <title>{ data.title }</title> </Head> <main> <MainLayout> <div className={styles.container}> <h1 className={styles.title}>{ data.title }</h1> <span className={styles.info}>Publicado { data.created_at.substring(8, 10) }/{ data.created_at.substring(5, 7) }/{ data.created_at.substring(0, 4) } por { data.author.name }</span> <p className={styles.content}>{ data.content }</p> </div> </MainLayout> </main> </> ); }
自定义Hook
export function useDocumentationGet(id) { const [data, setData] = useState([]); useEffect(() => { getDocumentationRequest(id) .then(res => { setData(res.data); }) }, []) return { data }; }
API请求函数
export async function getDocumentationRequest(id) { try { const response = await fetch( `${process.env.NEXT_PUBLIC_API_URL}/documentation/${id}`, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }, ); if (response.ok) { return { data: await response.json() }; } return { status: false, }; } catch (err) { return { status: false, }; } }
原因分析
- 初始状态类型不匹配:自定义Hook中
useState([])将初始data设为数组,但API返回的是单个文档对象。初始状态下data是数组,data.created_at必然为undefined,调用substring直接触发报错。 - 条件渲染逻辑不严谨:
data ? ... : ""中,初始data是数组(布尔值为true),仍会尝试访问data.created_at导致报错;若API请求失败,res.data不存在,data被设为undefined,此时条件渲染返回空字符串,导致<span>空白。 - 未处理异步加载状态:API请求是异步操作,组件初始渲染时
data还未获取到,直接访问其属性会触发错误。
解决方案
1. 修正初始状态并添加状态管理
将自定义Hook的初始data改为null(符合单对象返回类型),同时添加加载、错误状态,处理请求全生命周期:
export function useDocumentationGet(id) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(false); useEffect(() => { getDocumentationRequest(id) .then(res => { setData(res.data); setError(!res.status); }) .catch(() => setError(true)) .finally(() => setLoading(false)); }, [id]) return { data, loading, error }; }
2. 完善组件的条件渲染与安全访问
在页面组件中先处理加载、错误状态,使用可选链?.安全访问嵌套属性,同时提取日期格式化逻辑:
const DocumentationDetail = () => { const router = useRouter(); const { user } = useAuth(); const id = router.asPath.substring(1); const { data, loading, error } = useDocumentationGet(parseInt(id)); if (loading) return <div>Cargando...</div>; if (error || !data) return <div>Error al cargar la documentación</div>; const formatDate = (dateStr) => { if (!dateStr) return ""; return `${dateStr.substring(8, 10)}/${dateStr.substring(5, 7)}/${dateStr.substring(0, 4)}`; }; return ( <> <Head> <title>{data.title}</title> </Head> <main> <MainLayout> <div className={styles.container}> <h1 className={styles.title}>{data.title}</h1> <span className={styles.info}> Publicado {formatDate(data.created_at)} por {data.author?.name || "Desconocido"} </span> <p className={styles.content}>{data.content}</p> </div> </MainLayout> </main> </> ); }
3. 优化API请求返回逻辑
确保请求失败时明确返回data: undefined,避免后续处理混乱:
export async function getDocumentationRequest(id) { try { const response = await fetch( `${process.env.NEXT_PUBLIC_API_URL}/documentation/${id}`, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }, ); if (response.ok) { return { data: await response.json(), status: true }; } return { status: false, data: undefined }; } catch (err) { return { status: false, data: undefined }; } }
关键要点
- 初始状态必须与API返回的数据类型匹配,避免数组/对象混淆导致的属性访问错误。
- 必须处理异步请求的加载、成功、失败三种状态,既提升用户体验又避免报错。
- 使用可选链
?.和空值合并||可以更安全地访问嵌套属性,减少冗余的条件判断。
内容的提问来源于stack exchange,提问作者devkkit
相关产品推荐
相关产品推荐

