如何使用 ReactJS 显示来自 JSON 对象的数据?
这里有一位新程序员在学习 ReactJS。我有一些代码使用 axios 发出 HTTP 请求以从本地文件获取 XMLData。然后在响应中,我使用 xml-js 将该 XML 数据转换为 JSON 对象。然后,我获取该
jsonObj
并使用
setState()
将其保存到状态。
我有一个函数
renderTableRows()
,它应该返回 JSX 以在浏览器上显示我的 JSON 数据。我解构了状态并尝试从
renderTableRows()
进行控制台记录,但是当我尝试访问
users.result.body
时,我得到了
"TypeError: Cannot read property 'body' of undefined".
当我从
componentDidMount()
中的
then()
执行此操作时,我能够访问数据。我还在代码底部附上了我正在读取的数据的摘录。
我想使用
map()
遍历所有行数组属性。如能得到任何帮助,我将不胜感激。
class Table extends Component {
constructor(props) {
super(props)
this.state = {
users: []
}
}
async componentDidMount() {
axios.get(XMLData, {
"Content-Type": "application/xml; charset=utf-8"
})
.then((response) => {
var jsonObj = convert.xml2js(response.data,{compact:true, spaces: 4});
this.setState({users:jsonObj});
//console.log(this.state.users.result.body.row[0].col[0]);
});
}
renderTableHeader = () => {
return <th>
<td>Division Full Path</td>
<td>Billable Hours</td>
<td>Vacation Hours Only</td>
<td>Total Hours</td>
</th>
}
renderTableRows = () => {
const {users} = this.state
console.log(users.result.body);
return <h1>Hello from table rows</h1>
}
render() {
//const { users } = this.state
return <table>
<thead>
<tr>
{this.renderTableHeader()}
</tr>
</thead>
<tbody>
<tr>
{this.renderTableRows()}
</tr>
</tbody>
</table>
}
"header": {
"col": [
{
"label": {
"_text": "Counter Source Date"
}
},
{
"label": {
"_text": "Employee Id"
}
},
{
"label": {
"_text": "Counter Hours"
}
},
{
"label": {
"_text": " Division Full Path"
}
},
{
"label": {
"_text": " Projects/Equip/Vessels\nBillable"
}
},
{
"label": {
"_text": "Counter Name"
}
}
]
}
"body": {
"row": [
{
"col": [
{
"_text": "01/01/2021"
},
{
"_text": "2183"
},
{
"_text": "8.00"
},
{
"_text": "01 - Fort Lauderdale/Salvage"
},
{
"_text": "No"
},
{
"_text": "Holiday"
}
]
}
]
}
问题
初始状态与
renderTableRows
中的访问方式不匹配。
this.state = {
users: []
}
这里
this.state.users
是一个数组,因此
this.state.users.result
未定义。这一切都很好,直到您尝试访问
body
属性并引发错误
TypeError: Cannot read property 'body' of undefined
。
解决方案
您可以从有效的初始状态开始:
this.state = {
users: {
result: {}
}
}
或者在
renderTableRows
中使用一堆保护子句:
renderTableRows = () => {
const { users } = this.state
console.log(users.result && users.result.body);
return <h1>Hello from table rows</h1>
}
或者使用可选链接:
renderTableRows = () => {
const { users } = this.state
console.log(users.result?.body);
return <h1>Hello from table rows</h1>
}
由于您提到想要映射行,因此第一个选项不是您想要的。如果渲染行,它将类似于:
renderTableRows = () => {
const {users} = this.state
return users.map(user => (....))
}
更新
我建议将您的状态设置为
jsonObj.result
属性,这样您就不需要在每次渲染时访问
result
属性,它只是缩短了访问。将
this.state.users.headerColumns
映射到标题列,将
this.state.rows
映射到每一行,并另外映射行列。
class Table extends Component {
constructor(props) {
super(props);
this.state = {
users: {
headerColumns: [],
rows: [],
}
};
}
async componentDidMount() {
axios
.get(XMLData, {
"Content-Type": "application/xml; charset=utf-8"
})
.then((response) => {
var jsonObj = convert.xml2js(response.data, {
compact: true,
spaces: 4
});
this.setState({ users: {
headerColumns: jsonObj.header.col,
rows: jsonObj.body.row
} });
});
}
renderTableHeader = () => {
const { users: { headerColumns } } = this.state;
return (
<th>
{headerColumns.map(col => (
<td key={col.label._text}>{col.label._text}</td>
))}
<td>Total Hours</td>
</th>
);
};
renderTableRows = () => {
const { users: { rows } } = this.state;
return rows.map((row, index) => {
let computedTotal;
return (
<tr key={index}>
{row.col.map((value, index) => {
// compute time total from row data
return (
<td key={index}>{value}</td>
);
})}
<td>{computedTotal}</td>
</tr>
)});
};
render() {
return (
<table>
<thead>
<tr>{this.renderTableHeader()}</tr>
</thead>
<tbody>
{this.renderTableRows()}
</tbody>
</table>
);
}
}