在ReactJS应用程序中将字符串转换为JSON对象时出现错误

4
作为我不断学习ReactJS的一部分,我正在开发一个简单页面,将呈现以下趋势列表:

enter image description here

点击“获取趋势”按钮,从后端服务器通过WebSockets检索并显示趋势列表。它按预期工作。下面是相应的源代码:

import React, { Component } from 'react';
import './App.css';

class TrendRow extends Component {
  render() {
    return (
      <tr>
        <td>{this.props.onerow}</td>
      </tr>
    );
  }
}

class TrendTable extends Component {
  render() {
    var rows = [];
    for (var i=1; i<3; i++) {
      rows.push(<TrendRow key={i} onerow={this.props.trends[i]}/>);
    }
    return (
      <table>
        <thead>
          <tr>
            <th>List of Trends</th>
          </tr>
        </thead>
        <tbody>{rows}</tbody>
      </table>
    );
  }
}

class App extends Component {
  constructor() {
    super();

    this.state = {
      ws: new WebSocket('ws://localhost:8025/websockets/TwitterWSService'),
      getTrendsqueryString: 'GETTRENDS',
      listoftrends: ['{"trend":"#Default","id":"0"}']
    };

    this.handleOnClick = this.handleOnClick.bind(this);
  }

  handleOnClick = (event) => {
    this.state.ws.send(this.state.getTrendsqueryString);
    this.state.ws.onmessage = (event) => {
      this.setState(prevState => ({listoftrends: prevState.listoftrends.concat(event.data)}));
    }
  }

  render() {
    return (
      <div>
          <button onClick={this.handleOnClick}>Get Trends</button>
          <TrendTable trends={this.state.listoftrends} />
      </div>
    );
  }
}

export default App;

现在,当我尝试使用“JSON.parse”将显示的JSON字符串转换为JSON对象时,根据我的解析位置,会出现不同类型的错误。
如果我按照下面所示进行解析:
class TrendRow extends Component {
  render() {
    var jsonobject = JSON.parse(this.props.onerow);
    return (
      <tr>
        <td>{jsonobject}</td>
      </tr>
    );
  }
}

我遇到了以下错误:
"SyntaxError: Unexpected token u in JSON at position 0
var jsonobject = JSON.parse(this.props.onerow);"
对该错误信息进行快速谷歌搜索后,发现以下讨论,但是不太清楚如何将其解决应用于我的用例: uncaught syntaxerror unexpected token U JSON 我理解这个错误是由于初始渲染期间'this.props.onerow'的值未定义而JSON.parse()尝试解析此对象引起的。即使使用默认字符串初始化“listoftrends”对象也无法解决错误。
另一方面,如果像下面所示使用JSON.parse(),
  handleOnClick = (event) => {
    this.state.ws.send(this.state.getTrendsqueryString);
    this.state.ws.onmessage = (event) => {
      var jsonobject = JSON.parse(event.data);
      this.setState(prevState => ({listoftrends: prevState.listoftrends.concat(jsonobject)}));
    }
  }

我收到了错误消息:

对象不是React的有效子级...

谷歌搜索让我陷入了另一个兔子洞!请问有人能提供其他解决方案让我尝试吗?
5个回答

3

看了你的代码(以及你是初学者的说明),让我来添加一些评论,告诉你如何可能改进它。

  1. 使用箭头函数和类属性确保方法始终以组件作为值调用 this,意味着此处的手动绑定是多余的。所以你可以摆脱下面这行。

    this.handleOnClick = this.handleOnClick.bind(this);

  2. 还要摆脱 TrendTable 中丑陋的 for 循环,并用map 函数替代它。

    class TrendTable extends Component { render() { return ( <table> <tbody>{this.props.trends.map(trend => <TrendRow key={i} onerow={trend}/>)} </tbody> </table> ); } }

    您可以阅读更多关于避免常规 for 循环的替代方案。

  3. 对于预填充您的 trends 数组,更好的方法是使用 componentDidMount react 生命周期方法。要深入了解,请查看此文章。

    我认为最好创建一个更新按钮(而不是获取趋势),该按钮应完全使用来自后端的新数据更新您的趋势列表,以防需要(但当然,这一点取决于您)。

  4. 现在,如果您只需要初始化默认状态,则不必在组件内部使用构造函数方法,因此您可以仅在组件内部使用 state = {....};,而不使用构造函数。但请确保您正在使用第二阶段预设。

因此,考虑到上述评论,这是 App 组件:

class App extends Component {

  state = {
      ws: new WebSocket('ws://localhost:8025/websockets/TwitterWSService'),
      getTrendsqueryString: 'GETTRENDS',
      listoftrends: [] // you can add default trend record if you need it
    };
  };

  componentDidMount() {
    this.fetchTrends();
  }

  fetchTrends = (completeUpdate = false) => { 
    this.state.ws.send(this.state.getTrendsqueryString);
    this.state.ws.onmessage = (event) => {
      this.setState(prevState => (
        { listoftrends: !completeUpdate ? prevState.listoftrends.concat(event.data) : event.data }
      ));
    }    
  };

  updateTrends = () => {
    this.fetchTrends(true); //in that case you'd like to completely update the list
  }

}
  render() {
    return (
      <div>
        <button onClick={this.updateTrends}>Update trends</button>
        <TrendTable trends={this.state.listoftrends} />
      </div>
    );
  }
}
  1. 关于您的问题本身。正如其他人已经提到的,是不可能在JSX中使用一个对象的,因此您必须先对其进行转换(例如转换为数组)。

例如:

var array = Object.values(jsonobject).map(value => ...);

// and then in JSX
<div>{array}</div>

希望这一切都是有意义的。


1
我非常感谢您提供了详细的回复。这是一宝藏般的信息,我需要一些时间来吸收它们。现在我的页面已经按预期加载了趋势列表。不过我有一个评论:我使用了丑陋的for循环来选择用户选择的计数器变量的子集。我仍在尝试弄清楚如何使用map和filter功能来完成此操作。如果这很简单,请不要费心评论,我希望能自己解决它。 - Hari N

1

jsonobject 是一个对象。

React 不知道如何渲染它。

您可以将其转换为数组,然后再进行渲染。

var jsonArray = Object.keys(jsonobject).map(function(k) {
  return jsonobject[k];
});

JSX :

<td>{jsonobject}</td>


你的代码片段真的很有用。我的JSX看起来是这样的: "<td>{jsonArray[0]}</td>",它显示了趋势的值,例如"#Trend1"。 - Hari N

1
一个简单的解决方案是,在第一次渲染时绕过错误,不尝试解析JSON:
var jsonobject = this.props.onerow ? JSON.parse(this.props.onerow) : {};
正如其他人所说,你不能直接渲染对象。为了测试目的,请将 {jsonobject} 替换为 {jsonobject.id}。

0

如果您记录初始行和JSON解析结果,例如:

console.log(this.props.onerow);
console.log(JSON.parse(this.props.onerow));

控制台输出是什么?您的第一个错误可能是因为后端的 'JSON' 响应没有正确的结构,或者因为最初如果值是空字符串之类的,则无法在其上使用 JSON.parse,因为它会导致错误。

您的第二个错误 "对象不适用于React子元素" 是当您尝试在 JSX 元素内呈现一个对象时发生的情况,例如:

render(){
    let someObj = {potato:1, hello:'hi there'};
    return(<div>Here's the object: {someObj}</div>); <-- ERROR! can't put an object here
}

所以我猜想你在某个时候尝试在JSX元素中渲染JSON对象。


0

我已经根据用户提供的建议,提供了更新后的代码。这可能不是完美的,但实现了我想要的功能。

import React, { Component } from 'react';
import './App.css';

class TrendRow extends Component {
  render() {
    var jsonArray = Object.keys(this.props.onerow).map((k) => {
                                                  return this.props.onerow[k];
                                                  });
    return (
      <tr>
        <td>{jsonArray[0]}</td>
      </tr>
    );
  }
}

class TrendTable extends Component {
  render() {
    var rows = this.props.trends.map((trend, index) => {
          return (index<2) ? <TrendRow key={index} onerow={trend} /> : [];
        }
      );

    return (
      <table>
        <thead>
          <tr>
            <th>List of Trends</th>
          </tr>
        </thead>
        <tbody>{rows}</tbody>
      </table>
    );
  }
}

class App extends Component {
  constructor() {
    super();

    this.state = {
      ws: {},
      getTrendsqueryString: 'GET',
      listoftrends: []
    };
  }

  componentDidMount = () => {
    this.setState({ws: new WebSocket('ws://localhost:8025/websockets/TwitterWSService')});
  }

  handleOnClick = (event) => {
    this.state.ws.send(this.state.getTrendsqueryString);
    this.state.ws.onmessage = (event) => {
      var jsonobject = JSON.parse(event.data);
      this.setState(prevState => ({listoftrends: prevState.listoftrends.concat(jsonobject)}));
    }
    this.state.ws.onclose = (event) => {
      this.setState({ws: new WebSocket('ws://localhost:8025/websockets/TwitterWSService')});
    }
  }

  render() {
    return (
      <div>
          <button onClick={this.handleOnClick}>Get Trends</button>
        <TrendTable trends={this.state.listoftrends} />
      </div>
    );
  }
}

export default App;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接