Как разобрать строку, содержащую HTML, для реагирования на компоненты - PullRequest
0 голосов
/ 19 мая 2018

Я использую эту библиотеку: html-to-react, поскольку я обнаружил, что можно "скомпилировать" HTML как строку для взаимодействия компонентов.

Мы работаем с системойна основе виджетов, и они могут со временем меняться (например, добавлять / удалять / обновлять / и т. д.), поэтому мы планируем отправить HTML-код виджета из базы данных на фронт, что делается с помощью React.

I 'мы успешно создали простой виджет с использованием HTML, теперь я пытаюсь использовать этот виджет для реагирования на события нажатия, поэтому я подумал о добавлении метода onclick=method() из HTML или onClick={method} из React.Однако я не смог получить желаемый результат, так как я получаю эти ошибки в консоли моего браузера.

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)

Я использую следующий код:

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"
  }
}

Можно ли получить alert или что-то подобное, используя вышеупомянутую библиотеку для анализа HTML для React Components?Если так, как я мог это сделать?Или что я делаю не так?


Edit

Это не обязательно должна быть именно библиотека, которую я использую, если есть другая, с которой вы работали и что-то делалипохоже, я открыт для получения этих рекомендаций (обратите внимание, я не спрашиваю о программном обеспечении или библиотеке, а о том, как вызвать метод onClick внутри строки, содержащей мой HTML или обходной путь)


Edit 2

После попытки чего-то я обнаружил, что удаление этой строки:

var reactHtml = ReactDOMServer.renderToStaticMarkup(reactElement);

Избавляется от одного из сообщений.Библиотека использует этот элемент, чтобы проверить, совпадают ли текущий HTML-код и проанализированный.Я не нуждался в этом для моего случая, но это все еще оставляет текущую ошибку в консоли:

 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)

Редактировать 3

После некоторого исследования я обнаружил dangerousSetInnerHTML в этом ответе

Затем я попытался следовать этому ответу размещения alert внутри моего HTML следующим образом:

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

И это вызвало alert, однако, если я объявил myFunction, как показано в коде ниже (обратите внимание, я попытался объявить его как function вне класса, внутри него, а также как var myFunction = function() { ... } все вместе иразделены):

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;

Но на этот раз я получаю новую ошибку:

ReferenceError: myFunction is not defined[Learn More]

Я обнаружил похожий вопрос: Обработка клика по тегу внутри dangerouslySetInnerHTML/ регулярный html с этой проблемой, и попытался исследовать немного больше и нашел этот комментарий , который говорит, что события из dangerouslySetInnerHTML не будут инициированы таким образом, и ссылки на docs .

Итак, хотя это казалось обходным решением, когда я получил alertкогда написано непосредственно из onclick метода, или, возможно, я не сделал это правильно, так что если кто-то с большим опытом может попробовать и уточнить, было бы здорово.


Редактировать 4

Я нашел способ показать alert следующим образом:

  • Написать HTML как строку
  • Установить HTML с помощью dangerouslySetInnerHTML
  • На componentDidMound() функция от React использует чистый Javascript, чтобы добавить к ней onclick функцию.
  • Успех

Однако, то, что мы пытаемсяdo is :

  • Создайте несколько методов, например: addTwoNumbers или что-то в этом роде
  • Отправьте onclick или onClick из вызова HTML-строкиaddTwoNumbers function.
  • Повторяйте каждый раз, когда нам нужно добавить новый компонент, который должен использовать метод addTwoNumbers и просто написать HTML-код для него, сохранить его в базе данных и получить его, а затем простоиспользуйте его.

Что-то вроде:

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

Или, как я сказал выше:

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

Код, который всемне нужно получить alert следующее:

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;

Имея это в виду, знаете ли вы какой-нибудь другой способ сделать то, что я пытаюсь сделать?

Ответы [ 4 ]

0 голосов
/ 28 мая 2018

Самый простой способ сделать это - Опасно установить innerHTML

function createMarkup() {
  return {__html: 'First &middot; Second'};
}

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;

Но это идет с ценой: встраивание строки HTML в шаблон динамически вызывает нарушение безопасности, поэтому он имеет такой странный синтаксис __html истрашное имя опасно SetInnerHTML.Если вы используете это, убедитесь, что вы дезинфицируете свои входные параметры, чтобы предотвратить любое притяжение XSS. В качестве альтернативы вы можете использовать iframe

Проверить полную реализацию снизу пера (рекомендуется) https://codepen.io/varmakarthik12/pen/zaOzPw

0 голосов
/ 25 мая 2018

Почему бы не использовать API-интерфейс браузера DOMParser для анализа HTML-кода в автономном DOM, затем пройти по этому DOM и использовать React.createElement() для создания DOM React?Что-то вроде

let str = "... your HTML ..."

function traverse(node) {
  let children = []
  for (let i = 0; i < node.children.length; i++)
    children.push(traverse(node.children.item(i)))
  return React.createElement(node.localName, null, children)
  // or maybe use the following instead (React's API doc
  // isn't very clear about this):
  // return React.createElement(node.localName, null, ...children)
}

let reactElementForStr =
  traverse(new DOMParser().parseFromString(str, 'text/html'))

Имейте в виду, у меня есть только мимолетные знания о внутренней работе React, и я вообще не проверял это.

0 голосов
/ 28 мая 2018

Решение немного хакерское, скопируйте и вставьте в коды и поле

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>
    );
  }
}
0 голосов
/ 21 мая 2018

Я не знаю, сработает ли это в вашем конкретном случае, но идея в том, чтобы вообще не разбирать HTML.Вы можете указать webpack генерировать отдельные пакеты для разных наборов файлов.Я никогда не делал этого, но я несколько раз читал, что вы можете;например, this .

Тогда у вас может быть main.bundle.js, в котором есть все части вашего сайта, которые не меняются.Кроме того, создайте widget1.bundle.js и т. Д. Для каждого виджета, который может измениться.Импортируйте все это в ваш index.html.Затем настройте свой веб-сервер так, чтобы он никогда не кэшировал меньшие пакеты (widget1 и т. Д.).Когда пользователь заходит на сайт, он снова загружает эти виджеты, эффективно обновляя их.Если вы хотите проявить особую фантазию, вы можете предоставить API с текущей версией каждого виджета и добавить дополнительное поле в комплект виджетов вместе с версией, чтобы вы могли периодически проверять актуальность компонентов всобытие, когда пользователь не перезагрузил страницу в течение некоторого времени.Если вы обнаружите устаревший компонент, просто выполните принудительную перезагрузку, и браузер обязательно загрузит последнюю версию.

Я знаю, что это совершенно другой путь, чем тот, который вы собираетесь, но если он подходит для вашего конкретного пользователя.В этом случае, я полагаю, это будет проще, легче реализовать и гарантировать стабильность, более удобное в обслуживании и в целом лучшее решение, чем ручной анализ html и обработка всех запросов самостоятельно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...