4

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.

3
  • You may want to try to use react-html-parser npmjs.com/package/react-html-parser to convert HTML into React component and then use that for your purpose. Commented Jun 6, 2019 at 20:43
  • 1
    Not sure about parsing, but when you want to render string as raw HTML in react, the only API available for that is dangerouslySetInnerHTML Commented Jun 6, 2019 at 20:48
  • Check out react-hyperscript example in docs. You can use the lib if you want, or just implement it from scratch using the same idea since you were able to get them into DOM nodes already. Commented Jun 6, 2019 at 23:45

2 Answers 2

4

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)

Sign up to request clarification or add additional context in comments.

1 Comment

Doesn't support React 17+, for that take a look at this fork github.com/hedgedoc/html-to-react instead.
3

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.