Sanitized HTML można przekształcić React składniki, które mogą być uruchamiane zarówno na serwerze i kliencie przez parsowania ciąg HTML i przekształcenie uzyskanego węzły do React elementy.
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const str = `<div>divContent<p> para 1</p><p> para 2</p><gallery image-ids="" /><player video-id="" /><p> para 3</p><gallery image-ids="[1, 3]"/></div>`;
var parse = require('xml-parser');
const Gallery =() => React.createElement('div', null, 'Gallery comp');
const Player =() => React.createElement('div', null, 'Player comp');
const componentMap = {
gallery: Gallery,
player: Player
};
const traverse = (cur, props) => {
return React.createElement(
componentMap[cur.name] || cur.name,
props,
cur.children.length === 0 ? cur.content: Array.prototype.map.call(cur.children, (c, i) => traverse(c, { key: i }))
);
};
const domTree = parse(str).root;
const App = traverse(
domTree
);
console.log(
ReactDOMServer.renderToString(
App
)
);
Uwaga jednak, że nie jest JSX/TSX że naprawdę trzeba, jak wspomniano, ale drzewo React węzłów do renderowania React (ReactDOM w tym przypadku). JSX jest po prostu syntaktycznym cukrem, a przekształcanie go tam i z powrotem jest niepotrzebne, chyba że chcesz zachować wyjście React w twojej bazie kodów.
Ułagodzenie uproszczonego parsowania html. Jest to tylko w celach ilustracyjnych. Możesz użyć bardziej spec-zgodnej biblioteki, aby przeanalizować wejściowy html lub coś, co pasuje do twojego przypadku użycia.
Upewnij się, że pakiet po stronie klienta ma dokładnie ten sam komponent App
, bo inaczej skrypt klienta po stronie React mógłby ponownie utworzyć drzewo DOM, a stracisz wszystkie zalety renderowania po stronie serwera.
Możesz skorzystać z React 16's streamingu również z powyższym podejściem.
Rozwiązanie problemu rekwizyty
Rekwizyty będzie dostępna do ciebie z drzewa jako atrybuty i mogą być przekazywane jako rekwizyty (na starannym rozważeniu przypadku użycia oczywiście).
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const str = `<div>divContent<p> para 1</p><p> para 2</p><gallery image-ids="" /><player video-id="" /><p> para 3</p><gallery image-ids="[1, 3]"/></div>`;
var parse = require('xml-parser');
const Gallery = props => React.createElement('div', null, `Gallery comp: Props ${JSON.stringify(props)}`);
const Player =() => React.createElement('div', null, 'Player comp');
const componentMap = {
gallery: Gallery,
player: Player
};
const attrsToProps = attributes => {
return Object.keys(attributes).reduce((acc, k) => {
let val;
try {
val = JSON.parse(attributes[k])
} catch(e) {
val = null;
}
return Object.assign(
{},
acc,
{ [ k.replace(/\-/g, '') ]: val }
);
}, {});
};
const traverse = (cur, props) => {
const propsFromAttrs = attrsToProps(cur.attributes);
const childrenNodes = Array.prototype.map.call(cur.children, (c, i) => {
return traverse(
c,
Object.assign(
{},
{
key: i
}
)
);
});
return React.createElement(
componentMap[cur.name] || cur.name,
Object.assign(
{},
props,
propsFromAttrs
),
cur.children.length === 0 ? cur.content: childrenNodes
);
};
const domTree = parse(str).root;
const App = traverse(
domTree
);
console.log(
ReactDOMServer.renderToString(
App
)
);
Ostrożnie z niestandardowymi atrybutami - możesz chcieć postępować zgodnie z this rfc. Trzymaj z camelCase, jeśli to możliwe.
Wydaje mi się, że pytasz, jak zamienić ciągi takie jak ' ' na komponent reagujący - nie brzmi to zbyt rozsądnie. Czy mogę wiedzieć, dlaczego? Czy możesz w razie potrzeby zmienić zdanie? –
Pytanie już zawiera informacje o kontekście problemu. Łańcuch pochodzi z edytora WYSIWYG. – estus
A chcesz to zmienić w komponent reagujący? Konwencjonalnie było odwrotnie - Components => (react + react-dom) => htmlString. Pomijając sprawę, o której wspomniałeś, jak sobie z tym radzisz? Czego nie rozumiem, to czy szukasz automatycznego sposobu na przekształcenie html wygenerowanego z WYSIWYG w React Components? Czy istnieje lista niestandardowych elementów, które chcesz przenieść do biblioteki React/React-like. –