web-dev-qa-db-fra.com

Comment analyser une chaîne contenant du HTML pour React Components

J'utilise cette bibliothèque: html-to-react car j'ai trouvé qu'il était possible de "compiler" du HTML en tant que chaîne pour React Composants.

Nous faisons un système basé sur des widgets et ils peuvent changer au fil du temps (c'est-à-dire ajouter/supprimer/mettre à jour/etc), nous prévoyons donc d'envoyer le widget HTML de la base de données vers l'avant, ce qui se fait à l'aide de React.

J'ai réussi à créer un widget simple en utilisant HTML, maintenant j'essaie ce widget pour répondre aux événements de clic, alors j'ai pensé à ajouter la méthode onclick=method() à partir du HTML ou onClick={method} À partir de React. Cependant, je n'ai pas pu obtenir la sortie souhaitée car je reçois ces erreurs dans la console de mon navigateur.

Warning: Invalid event handler property `onclick`. React events use the camelCase naming convention, for example `onClick`.
    in label
    in span
    in div
Warning: Invalid event handler property `onclick`. Did you mean `onClick`?
    in label
    in span
    in div
    in DireccionComponent (at App.js:51)
    in div (at App.js:50)
    in GridItem (created by ReactGridLayout)
    in div (created by ReactGridLayout)
    in ReactGridLayout (at App.js:49)
    in App (at index.js:11)

Le code que j'utilise est le suivant:

App.js

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

class App extends Component {
    render() {
        return (
            <DireccionComponent />
        );
    }
}

export default App;

DireccionComponent.js

import React, {Component} from 'react';

var ReactDOMServer = require('react-dom/server');
var HtmlToReactParser = require('html-to-react').Parser;

let htmlInput = ''
            +   '<div>'
            +       '<center><label className="title">Widget</label></center>'
            +       '<span className="ui-float-label">'
            +           '<label htmlFor="float-input" onClick={this.myFunction}>Hello</label>'
            +       '</span>'
            +   '</div>';

var htmlToReactParser = new HtmlToReactParser();
var reactElement = htmlToReactParser.parse(htmlInput);
var reactHtml = ReactDOMServer.renderToStaticMarkup(reactElement);

class DireccionComponent extends Component {
    constructor(props) {
        super (props);

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

    render() {
        return (
            reactElement
        )
    }

    myFunction() {
        console.log('Hola');
        alert("Hola");
    }
}

export default DireccionComponent;

package.json

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "html-to-react": "^1.3.3",
    "primereact": "^1.5.1",
    "react": "^16.3.2",
    "react-dom": "^16.3.2",
    "react-dom-server": "0.0.5",
    "react-grid-layout": "^0.16.6",
    "react-scripts": "1.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

Est-il possible d'obtenir un alert ou quelque chose de similaire en utilisant la bibliothèque ci-dessus pour analyser HTML vers React Composants? Si oui, comment pourrais-je le faire? Ou qu'est-ce que je fais mal ?


Modifier

Il n'est pas nécessaire que ce soit spécifiquement la bibliothèque que j'utilise, s'il y en a une autre avec laquelle vous avez travaillé et fait quelque chose de similaire, je suis ouvert à recevoir ces recommandations (Remarque, je ne demande pas de logiciel ou de bibliothèque , mais comment appeler la méthode onClick dans la chaîne contenant mon code HTML ou une solution de contournement)


Modifier 2

Après avoir essayé quelque chose, j'ai trouvé que supprimer cette ligne:

var reactHtml = ReactDOMServer.renderToStaticMarkup(reactElement);

Supprime l'un des messages. La bibliothèque utilise cet élément pour tester si le HTML actuel et celui analysé sont identiques. Je n'en avais pas besoin pour mon cas mais cela laisse toujours l'erreur actuelle dans la console:

 Warning: Invalid event handler property `onclick`. Did you mean `onClick`?
    in label
    in span
    in div
    in DireccionComponent (at App.js:47)
    in App (at index.js:11)

Modifier 3

Après quelques recherches, j'ai trouvé dangerousSetInnerHTML dans cette réponse

J'ai ensuite essayé de suivre cette réponse en plaçant un alert dans mon HTML comme suit:

'<label htmlFor="float-input" onclick="alert(\'Hello\')">Hello2</label>'

Et cela a déclenché le alert, cependant si j'ai déclaré myFunction comme dans le code ci-dessous (remarquez que j'ai essayé de le déclarer comme function en dehors de la classe, à l'intérieur et aussi comme var myFunction = function() { ... } tous ensemble et séparés):

import React, {Component} from 'react';

var ReactDOMServer = require('react-dom/server');
var HtmlToReactParser = require('html-to-react').Parser;

let htmlInput = ''
            //+ '<div>'
            +       '<center><label className="title">Widget</label></center>'
            +       '<span className="ui-float-label">'
            +           '<label htmlFor="float-input" onclick="myFunction()">Hello2</label>'
            +       '</span>'
            //+     '</div>';

var htmlToReactParser = new HtmlToReactParser();
var reactElement = htmlToReactParser.parse(htmlInput);

class DireccionComponent extends Component {
    constructor(props) {
        super (props);

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

    render() {
        return (
            <div dangerouslySetInnerHTML={{__html: htmlInput}}>

            </div>
        )
    }

    myFunction() {
        console.log('Hola');
        alert("Hola");
    }
}

function myFunction() {
    console.log('Hola');
    alert("Hola");
}

export default DireccionComponent;

Mais cette fois, j'obtiens une nouvelle erreur:

ReferenceError: myFunction is not defined[Learn More]

J'ai trouvé une question similaire: Gestion onClick d'une balise <a> à l'intérieur de dangerouslySetInnerHTML/regular html avec ce problème, et j'ai essayé d'enquêter un peu plus et j'ai trouvé ce commentaire qui indique que les événements de dangerouslySetInnerHTML ne seront pas déclenchés de cette façon et établit un lien vers docs .

Donc, même si cela semblait être une solution de contournement lorsque j'ai obtenu un alert lorsqu'il a été écrit directement à partir de la méthode onclick, ou probablement je ne l'ai pas fait de la bonne manière, donc si quelqu'un avec plus l'expérience pourrait l'essayer et clarifier, serait formidable.


Modifier 4

J'ai trouvé un moyen d'afficher un alert de cette manière:

  • Écrire le HTML sous forme de chaîne
  • Définissez le HTML via dangerouslySetInnerHTML
  • Sur la fonction componentDidMound() de React utilisez du Javascript pur pour y ajouter la fonction onclick.
  • Succès

Cependant, ce que nous essayons de faire est :

  • Créez des méthodes, par exemple: addTwoNumbers ou quelque chose comme ça
  • Envoyez un onclick ou onClick depuis la chaîne HTML appelant la fonction addTwoNumbers.
  • Répétez chaque fois que nous devons ajouter un nouveau composant qui doit utiliser la méthode addTwoNumbers et juste pour écrire le code HTML pour celui-ci, l'enregistrer sur la base de données et le récupérer et ensuite l'utiliser.

Quelque chose comme:

...
    <label onClick={myFunction}> My Label </label>
...

Ou comme je l'ai dit plus haut:

...
    <label onClick={addTwoNumbers}> My Label </label>
...

Le code qui me permet d'obtenir un alert est le suivant:

import React, {Component} from 'react';
import Parser from 'html-react-parser';
import {render} from 'react-dom';

var ReactDOMServer = require('react-dom/server');
var HtmlToReactParser = require('html-to-react').Parser;

let htmlInput = ''
            //+ '<div>'
            +       '<center><label className="title">Widget</label></center>'
            +       '<span className="ui-float-label">'
            +           '<label htmlFor="float-input" id="myLabel">Hello2</label>'
            +       '</span>'
            //+     '</div>';

var htmlToReactParser = new HtmlToReactParser();
var reactElement = htmlToReactParser.parse(htmlInput);

class DireccionComponent extends Component {
    constructor(props) {
        super (props);

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

    render() {
        return (
            <div dangerouslySetInnerHTML={{__html: htmlInput}}>

            </div>
        )
    }

    componentDidMount() {
        console.log('DONE');

        let myLabel = document.getElementById("myLabel");

        myLabel.onclick = function() {
            alert();
            console.log('clicked');
        }
        console.log(myLabel);
    }

    myFunction() {
        console.log('Hola');
        alert("Hola");
    }
}

function myFunction() {
    console.log('Hola');
    alert("Hola");
}

export default DireccionComponent;

Dans cet esprit, connaissez-vous une autre façon de faire ce que j'essaie de faire?

15
Frakcool

La solution est un peu hacky, copiez-collez dans codesandbox

class App extends Component {
  constructor(props) {
    super(props);
    this.myfunc = this.myfunc.bind(this);
  }
  componentDidMount() {
    window.myfunc = this.myfunc;
  }
  myfunc() {
    console.log("from myfunc");
  }
  componentWillUnmount() {
    window.myfunc = null;
  }
  render() {
    let inputhtml = "<a onclick=window.myfunc()>HelloWorld</a>";
    return (
      <div style={styles}>
        <div dangerouslySetInnerHTML={{ __html: inputhtml }} />
      </div>
    );
  }
}
3
Rahil Ahmad

Je ne sais pas si cela fonctionnerait pour votre cas particulier, mais une idée est de ne pas analyser HTML du tout. Vous pouvez demander à webpack de générer des ensembles séparés pour différents ensembles de fichiers. Maintenant, je ne l'ai jamais fait, mais j'ai lu plusieurs fois ce que vous pouvez; par exemple, this .

Ensuite, vous pouvez avoir un main.bundle.js qui contient toutes les parties de votre site qui ne changent pas. Créez également un widget1.bundle.js et ainsi de suite pour chaque widget qui peut changer. Importez tous ceux sur votre index.html. Ensuite, configurez votre serveur Web pour ne jamais mettre en cache les petits bundles (widget1, etc.). Chaque fois que l'utilisateur accède au site Web, il télécharge à nouveau ces widgets, les mettant à jour efficacement. Si vous voulez être plus sophistiqué, vous pouvez fournir une API avec la version actuelle de chaque widget, et mettre un champ supplémentaire sur le bundle de widgets avec la version, afin que vous puissiez vérifier périodiquement si les composants sont à jour, dans le événement que l'utilisateur n'a pas rechargé la page depuis un certain temps. Si vous découvrez un composant obsolète, forcez simplement le rechargement et le navigateur s'assurera de récupérer la dernière version.

Je sais que c'est un chemin totalement différent de celui que vous utilisez, mais si c'est bon pour votre cas particulier, je pense qu'il sera plus simple, plus facile à mettre en œuvre et garantira la stabilité, plus maintenable et globalement une meilleure solution que l'analyse manuelle du html et gérer vous-même toutes les demandes.

0
Luan Nico