2017-09-08 1 views
4

Dans le cadre de mes efforts en cours pour apprendre ReactJS que je développe une simple page qui produira une liste des tendances comme suit:Erreur lors de la conversion d'une chaîne à l'objet JSON dans l'application ReactJS

enter image description here

En cliquant sur le bouton "Get Trends" la liste des tendances est récupérée à partir d'un serveur back-end sur websockets et affichée. Cela fonctionne comme prévu. Ci-dessous le code source correspondant:

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; 

Maintenant, lorsque je tente de convertir la chaîne JSON affichée dans un objet JSON en utilisant « JSON.parse » je reçois différents types d'erreurs en fonction de là où je l'analyse syntaxique.

Si je Parse comme indiqué ci-dessous,

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

Je reçois l'erreur suivante:

"SyntaxError: Unexpected token u in JSON at position 0

...

var jsonobject = JSON.parse(this.props.onerow);

..."

Une recherche rapide sur Google pour le message d'erreur renvoyé la discussion qui suit, mais ce n'est pas très clair comment appliquer la solution à mon cas d'utilisation:

uncaught syntaxerror unexpected token U JSON

Je comprends que l'erreur est due à la valeur 'this.props.onerow' n'étant pas définie pendant le rendu initial et JSON.parse() essayant d'analyser cet objet. Même l'initialisation de l'objet "listoftrends" avec une chaîne par défaut ne résout pas l'erreur.

Si d'autre part, je JSON.parse(), comme indiqué ci-dessous,

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)})); 
    } 
    } 

Je reçois le message d'erreur:

Objects are not valid as a React child...

recherche Google me mène un autre trou de lapin! Quelqu'un pourrait-il me fournir d'autres solutions?

Répondre

3

En regardant votre code (et votre note que vous venez d'apprendre) laissez-moi ajouter quelques commentaires comment vous pouvez éventuellement l'améliorer.

  1. en utilisant une fonction de flèche avec une propriété de classe assure que la méthode est toujours invoquée avec le composant comme valeur this, ce qui signifie que le manuel de liaison ici est redondant. Donc, vous pouvez vous débarrasser de la ligne ci-dessous.

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

  2. pouvez également obtenir débarrasser de la boucle for laide à l'intérieur du TrendTable et le remplacer par map fonction.

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

    You can read more sur ce que vous avez des solutions de rechange si vous allez éviter la boucle régulière for. Pour pré-remplir votre tableau trends, la meilleure approche consiste à utiliser la méthode du cycle de vie componentDidMount. Pour plonger plus profond vérifier this article.

    Et je pense qu'il est préférable de créer un bouton de mise à jour (plutôt que d'obtenir des tendances) qui devrait complètement réécrire votre liste de tendances avec une nouvelle partie du backend (mais ce point vous appartient).

  3. Maintenant, vous ne devez pas utiliser la méthode constructeur dans le composant si vous venez seulement besoin d'initialiser l'état par défaut, vous pouvez utiliser state = {....}; juste à l'intérieur de votre composant w/o utilisation du constructeur. Mais assurez-vous que vous utilisez stage-2 preset.

Donc, en prenant en compte les commentaires ci-dessus, voici le composant 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. Et en ce qui concerne votre question elle-même. Comme beaucoup d'autres gars déjà mentionné, oui, il n'est pas possible d'utiliser un objet à l'intérieur de JSX, donc vous devez le transformer en premier (par exemple en tableau).

Par ex

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

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

Espérons que tout cela a du sens.

+1

J'apprécie vraiment que vous donniez une réponse détaillée. C'était un trésor d'informations et il me faudra du temps pour tout assimiler. Ma page se charge maintenant avec la liste des tendances comme prévu. J'ai un commentaire cependant: J'ai utilisé la boucle pour pour sélectionner un sous-ensemble des tendances en utilisant une variable compteur sélectionné par l'utilisateur. J'essaie toujours de comprendre comment utiliser la carte et les capacités de filtrage pour y parvenir. Si c'est trivial ne vous embêtez pas à commenter et j'espère pouvoir le comprendre moi-même. –

0

Si vous consignez la ligne initiale, les résultats AND de votre analyse JSON, par ex.

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

Quelle est la sortie de la console? Votre première erreur est probablement parce que votre réponse 'JSON' du backend n'est pas correctement structurée, ou parce qu'au départ si la valeur est quelque chose comme une chaîne vide, vous ne pouvez pas utiliser JSON.parse car cela provoquera une erreur.

Votre deuxième erreur, Objects are not valid as a React child, est ce qui se passe lorsque vous essayez de rendre un objet à l'intérieur d'un élément JSX, par exemple:

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

Je devine à un moment donné que vous essayez de rendre cette Objet JSON dans un élément JSX.

1

jsonobject est un objet.

Réagissez je ne sais pas comment le rendre.

Vous pouvez le convertir en un tableau, puis le rendre.

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

JSX:

<td>{jsonobject}</td>

+0

Votre extrait de code m'a vraiment aidé. Mon JSX ressemble à ceci: " {jsonArray [0]}" et il affiche les valeurs de tendance, par ex. "# Trend1". –

1

Une solution simple pour effectuer votre premier travail d'approche serait de contourner l'erreur de ne pas tenter d'analyser JSON au cours du premier rendu:

var jsonobject = this.props.onerow ? JSON.parse(this.props.onerow) : {};

Comme d'autres l'ont déjà dit, vous ne pouvez pas rendre les objets directement. À des fins de test, remplacez <td>{jsonobject}</td> par <td>{jsonobject.id}</td>.

0

J'ai fourni le code mis à jour après avoir incorporé les suggestions fournies par les utilisateurs. Cela peut ne pas être parfait mais accomplit ce que je voulais.

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;