为什么我的 JSON 响应在 forEach 上崩溃?未定义类型错误
Why does my JSON response crash on forEach? With TypeError undefined
我设置了一个 Cloud Firebase 数据库并向它发出了获取请求,但是当我尝试遍历 returned 对象中的数组时,我得到 TypeError: Cannot read property 'forEach' of undefined
并且我是不知道为什么..我看到一些帖子建议 JSON.parse res 但是它没有改变输出..
import React, { useState, useEffect } from "react";
import axios from "axios";
const Submissions = () => {
const [state, setState] = useState({});
useEffect(() => {
axios
.get("/submissions")
.then((res) => {
setState({
submissions: res.data,
});
})
.catch((err) => {
console.error(err);
});
// eslint-disable-next-line
}, []);
return (
<div style={{ paddingTop: "3rem" }}>
<h1>Submissions</h1>
{state.submissions !== null &&
state.submissions.forEach((element) => {
<p>{element.patientName}</p>;
})}
</div>
);
};
export default Submissions;
如果我return以下而不是迭代数组:
<p>{JSON.stringify(state)}</p>
但是我确实得到了我预期的虚拟数据响应:
{"submissions":[{"submissionId":"NxkofnOyiZSjpBa918it","patientName":"Bobby Hill","onsetDate":"2021-05-28T01:41:44.368Z","immuno":true,"highrisk":false,"symptoms":true,"collectionDate":"2021-05-28T01:41:44.368Z","createdAt":"2021-05-28T01:42:00.042Z"},{"submissionId":"PuyDsvIlNC5Mi1h5u7b5","patientName":"Theo","onsetDate":"2021-05-28T01:20:06.275Z","immuno":false,"highrisk":false,"symptoms":false,"collectionDate":"2021-05-28T01:20:06.275Z","createdAt":"2021-05-28T01:20:06.275Z"}]}
最初 state.submissions
将是 undefined
,而 undefined !== null
是 true,因为它们不是 strictly equal。
state.submissions !== null
=> undefined !== null
=> true
因为即使没有数据,state.submissions !== null
的计算结果仍为真,您最终尝试在 undefined
上调用 forEach
。
要解决这个问题,您可以
- 做一个非同一性equality comparison:
state.submissions != null
,或者
- 只需通过
{ state.submissions && ( ... )}
测试 truthiness
除了 API 响应中的对象之外,您还将状态设置为具有自己的提交 属性 的对象。
所以你的数据,它有一个提交 属性,在你的组件状态自己的提交下 属性。
有效:setState( { submissions: { submissions: […] }}
因此您必须访问 state.submissions.submissions
才能访问数组。
改为直接设置响应:setState(res.data)
您将 运行 遇到的下一个问题是您的 forEach 中的任何内容都不会呈现,因为该函数不会 return 任何内容。将其更改为 map 和 return 每次迭代的标记:
{state.submissions &&
state.submissions.map((element) => {
return <p>{element.patientName}</p>;
})
}
如果您的环境支持(或 polyfill)optional chaining you can simplify this even further by eliminating the conditional altogether, and while we're at it use the arrow function's implicit return:
state.submissions?.map?.((element) => <p>{element.patientName}</p>)
import React, { useState, useEffect } from "react";
import axios from "axios";
const Submissions = () => {
const [submissions, setSubmissions] = useState([]);
useEffect(() => {
axios
.get("/submissions")
.then((res) => {
setSubmissions(res.data);
})
.catch((err) => {
console.error(err);
});
// eslint-disable-next-line
}, []);
return (
<div style={{ paddingTop: "3rem" }}>
<h1>Submissions</h1>
{submissions.length &&
submissions.forEach((element) => {
<p>{element.patientName}</p>;
})}
</div>
);
};
编辑:我推荐上面的代码。我假设你在数据之间分离了状态并给出了我的答案。以下答案不适用于您的情况。反正我是留着,以备不时之需
问题是你的渲染部分是在useEffect之前执行的。所以在第一个渲染中你的状态将是 {}
。您必须通过使状态 []
而不是 {}
.Object
does not have forEach
.
来处理这种情况
我设置了一个 Cloud Firebase 数据库并向它发出了获取请求,但是当我尝试遍历 returned 对象中的数组时,我得到 TypeError: Cannot read property 'forEach' of undefined
并且我是不知道为什么..我看到一些帖子建议 JSON.parse res 但是它没有改变输出..
import React, { useState, useEffect } from "react";
import axios from "axios";
const Submissions = () => {
const [state, setState] = useState({});
useEffect(() => {
axios
.get("/submissions")
.then((res) => {
setState({
submissions: res.data,
});
})
.catch((err) => {
console.error(err);
});
// eslint-disable-next-line
}, []);
return (
<div style={{ paddingTop: "3rem" }}>
<h1>Submissions</h1>
{state.submissions !== null &&
state.submissions.forEach((element) => {
<p>{element.patientName}</p>;
})}
</div>
);
};
export default Submissions;
如果我return以下而不是迭代数组:
<p>{JSON.stringify(state)}</p>
但是我确实得到了我预期的虚拟数据响应:
{"submissions":[{"submissionId":"NxkofnOyiZSjpBa918it","patientName":"Bobby Hill","onsetDate":"2021-05-28T01:41:44.368Z","immuno":true,"highrisk":false,"symptoms":true,"collectionDate":"2021-05-28T01:41:44.368Z","createdAt":"2021-05-28T01:42:00.042Z"},{"submissionId":"PuyDsvIlNC5Mi1h5u7b5","patientName":"Theo","onsetDate":"2021-05-28T01:20:06.275Z","immuno":false,"highrisk":false,"symptoms":false,"collectionDate":"2021-05-28T01:20:06.275Z","createdAt":"2021-05-28T01:20:06.275Z"}]}
最初 state.submissions
将是 undefined
,而 undefined !== null
是 true,因为它们不是 strictly equal。
state.submissions !== null
=> undefined !== null
=> true
因为即使没有数据,state.submissions !== null
的计算结果仍为真,您最终尝试在 undefined
上调用 forEach
。
要解决这个问题,您可以
- 做一个非同一性equality comparison:
state.submissions != null
,或者 - 只需通过
{ state.submissions && ( ... )}
测试 truthiness
除了 API 响应中的对象之外,您还将状态设置为具有自己的提交 属性 的对象。
所以你的数据,它有一个提交 属性,在你的组件状态自己的提交下 属性。
有效:setState( { submissions: { submissions: […] }}
因此您必须访问 state.submissions.submissions
才能访问数组。
改为直接设置响应:setState(res.data)
您将 运行 遇到的下一个问题是您的 forEach 中的任何内容都不会呈现,因为该函数不会 return 任何内容。将其更改为 map 和 return 每次迭代的标记:
{state.submissions &&
state.submissions.map((element) => {
return <p>{element.patientName}</p>;
})
}
如果您的环境支持(或 polyfill)optional chaining you can simplify this even further by eliminating the conditional altogether, and while we're at it use the arrow function's implicit return:
state.submissions?.map?.((element) => <p>{element.patientName}</p>)
import React, { useState, useEffect } from "react";
import axios from "axios";
const Submissions = () => {
const [submissions, setSubmissions] = useState([]);
useEffect(() => {
axios
.get("/submissions")
.then((res) => {
setSubmissions(res.data);
})
.catch((err) => {
console.error(err);
});
// eslint-disable-next-line
}, []);
return (
<div style={{ paddingTop: "3rem" }}>
<h1>Submissions</h1>
{submissions.length &&
submissions.forEach((element) => {
<p>{element.patientName}</p>;
})}
</div>
);
};
编辑:我推荐上面的代码。我假设你在数据之间分离了状态并给出了我的答案。以下答案不适用于您的情况。反正我是留着,以备不时之需
问题是你的渲染部分是在useEffect之前执行的。所以在第一个渲染中你的状态将是 {}
。您必须通过使状态 []
而不是 {}
.Object
does not have forEach
.