Reputation: 1072
I'm retrieving an HTML string from SharePoint and need to parse and modify the data and build a react element to be displayed in my react application.
Basically, I have code (as string) returning to me in a format similar to:
"
<div>
<div data-sp-canvasdataversion="1">This could be a header</div>
<div data-sp-canvasdataversion="1"><img src="titleimage.jpg"></div>
<div data-sp-canvasdataversion="1"><a href="pdfLink.pdf">This is a link to a PDF</a></div>
</div>
"
and from that I need to cycle through the children and build a new React element containing some parts of what was returned and some new parts such as
<div>
<div data-sp-canvasdataversion="1">This could be a header</div>
<div data-sp-canvasdataversion="1"><img src="titleimage.jpg"></div>
<PDFViewer file={""pdfLink.pdf""}></PDFViewer>
</div>
I was originally using dangerouslySetInnerHTML which worked to simply display the data, but now i need to remove a chunk of the html, create a react element based on the data and inject the new element back into the code. Since i'm trying to insert a component now, vanilla html won't work.
I'm able to cycle through the children by converting it to a dom node but I can't figure out how to use a dom node or element as a React Element Child.
I've tried:
let element = React.createElement('div', {}, HTMLString);
let node = document.createRange().createContextualFragment(HTMLString).firstChild;
let element = React.createElement('div', {}, node);
let node = document.createRange().createContextualFragment(HTMLString).firstChild;
let node2 = document.createElement("div");
node2.appendChild(node);
node2 = node2.firstElementChild as HTMLDivElement;
let element = React.createElement('div', {}, node2);
None work as needed and give me the error Objects are not valid as a React child (found: [object HTMLDivElement]).
or similar.
What i need is something like:
let element = React.createElement('div');
let node = document.createRange().createContextualFragment(HTMLString).firstChild;
node.childNodes.forEach(child => {
if(...//child IS NOT pdf)
element.appendChild(child)
else if(...//child IS pdf){
...
element.appendChild(<PDFViewer file="linktopdf.pdf">)
}
})
Then I would expect to be able to use that in render
render {
...
return(
<div className="container">
{element}
</div>
);
}
Please let me know if this is even possible and how. The only possible solution I could think of was maybe saving the child elements as strings and using dangerouslySetInnerHTML to generate all of them but I really want to get away from using dangerouslySetInnerHTML, especially like that.
Upvotes: 4
Views: 15143
Reputation: 324
For below React v17.0: try this react-html-parser
For React v17.0 and above: try this fork (thanks to @thesdev pointing that out)
Upvotes: 4
Reputation: 1072
I ended up doing what I didn't want to which is using dangerouslySetInnerHTML. If anyone comes up with a better solution, please feel free to share.
async getArticleComponents(data: any) {
if (!data)
return null;
let slides = []
let node = document.createRange().createContextualFragment(data).firstChild;
let children = node.childNodes;
for (let i = 0; i < children.length; i++) {
var element = (children[i] as HTMLDivElement);
var controlElement = element.attributes.getNamedItem("data-sp-controldata");
var jObj = controlElement ? JSON.parse(controlElement.value) : null;
if (jObj.controlType === 3) {
var childEle = element.firstElementChild;
var webPartData = childEle.attributes.getNamedItem("data-sp-webpartdata");
var wpJObj = webPartData ? JSON.parse(webPartData.value) : null;
if (wpJObj.title == "File viewer") {
let ext = (/.*\.(.*)/i).exec(wpJObj.properties.file)[1].toLowerCase();
if (ext === "pdf") {
var pdf = await fetch(`/api/SharePoint/GetFile?url=${encodeURIComponent(`${wpJObj.serverProcessedContent.links.serverRelativeUrl}`)}`)
.then(res => res.text())
.then(pdf => pdf ? `data:application/pdf;base64,${pdf}` : null);
slides.push(<PDFViewer key={jObj.id} file={{ pdf, fileName: "test" } as IPdfMeta} />);
}
} else
slides.push(<div key={jObj.id} dangerouslySetInnerHTML={{ __html: element.outerHTML }}></div>);
}
else if (jObj.controlType !== 0) {
slides.push(<div key={jObj.id} dangerouslySetInnerHTML={{ __html: element.outerHTML }}></div>);
}
}
return slides;
}
Upvotes: 3