开发者问题收集

如何使用 ReactJS 显示来自 JSON 对象的数据?

2021-05-02
392

这里有一位新程序员在学习 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"
                    }
                ]
            }
          ]
        }
1个回答

问题

初始状态与 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>
    );
  }
}
Drew Reese
2021-05-02