Create Html from React Component including Style - reactjs

I have a simple react component in which using ref I am getting the div but I have to generate a html includes styling as well. So I can pass this html to PDF generation backend server.
pdfRef(elem){
console.log(elem);
//<div><span>dataPDF</span> </div>
}
render() {
return (
<div ref={(elem) => this.pdfRef(elem)} className="SomeCssClass">
<span >dataPDF</span>
</div>
);
}
}
[Edit]
When I try to print the div via ref, the elements are printed with class name. But when I send this string to pdf service, since only html element is sent and class name without the actual css , the pdf is generated without style.
is there any way to generate html with css as as string so further it can be send to pdf service. Hope the question is clear
Any pointers?

The server.js script in react-dom lets you render a React component to static html string. You can require it in your code like:
const ReactDOMServer = require('react-dom/server');
Or using ES6 syntax:
import ReactDomServer from 'react-dom/server'
After this you can render your component to HTML string using the ReactDomServer.renderToString or ReactDomServer.renderToStaticMarkup functions as follows:
const htmlStr = ReactDomServer.renderToStaticMarkup(<MyComponent prop1={'value1'} />);
This looks almost exactly like ReactDom.render, except that it doesn't need the second parameter of dom node to render at, and the html string is returned. Additionally this method can be used on both client and server side. For your generate-pdf use case renderToStaticMarkup would suffice. If required check the documentation at following link for subtle difference between the two methods mentioned above: https://reactjs.org/docs/react-dom-server.html

Related

findByText over multiple components

I'm looking to do some testing on some components. However, it fails given that some components are nested, but I want to simply check if the text is rendered on the screen.
For example:
<div>Hello <br />World</div>
<div>Hello <p>World</p></div>
<p>Hello</p><p> World</p>
I'm not looking to see if they're rendered inside the correct components, but to see if the text "Hello World" is rendered at all.
From what I'm seeing in my testing, it is not.
You will need to inspect the textContent property of the HTML node, which includes all inner text as a string.
With JS:
const component = render(...)
expect(component.container.innerContent).toContain(...)
With Jest extend-expect:
const component = render(...)
expect(component.container).toHaveTextContent(...)

Replace DOM with JSX in React 18

How to remove this error message from my console
I'm using ReactDOM.render to replace certain "unreachable" parts of my code with JSX components, it worked fine in previous versions but now I'm getting this annoying error message and I want to get rid of it.
Long story:
I'm using the FullCalendar lib for react18 and Nextjs.
I'm facing a limitation from the lib, in previous versions I was able to pass JSX to render in the header buttons, but in the current version 5.11.2 it's not possible anymore, it only let you set either text or a bootstrap/font-awesome icon.
So I instead used an old known trick to replace DOM with no more than the HTML element
ReactDOM.render(
<AnyIconIWantToUse />,
window.document.querySelector("#element-to-replace-id")
)
and that is what brings up the said error message
What I've tried
As the error suggest I've tried using createRoot instead but it gives me an error too (and afaik it's meant to be used only with the root component so I prefer not to use it).
This should help you out
createPortal(
<AnyIconIWantToUse />,
document.getElementById("element-to-replace-id")
)
I ended up achieving what I wanted with another approach.
Instead of replacing DOM content directly with JSX I instead render the desired JSX into the DOM and replace the DOM with DOM
// utils/replaceDOM.ts
import type React from 'react';
import { renderToString } from 'react-dom/server';
type ReplaceDOM = (
elementToReplace: Element,
replacement: React.ReactElement
) => void;
const replaceDOM: ReplaceDOM = (elementToReplace, replacement) => {
if (!replacement) return;
// Get html from component (only get first render)
const replacementHTML = renderToString(replacement);
// Parse html string into html
const parser = new DOMParser();
const parsedDocument = parser.parseFromString(replacementHTML, 'text/html');
const replacementElement = parsedDocument.body.children[0];
// Append replacement to DOM
window.document.body.prepend(replacementElement);
// Replace children with element
elementToReplace.replaceWith(replacementElement);
};
export default replaceDOM;
Then I can use it as desired
replaceDOM(elementToReplace, <ElementIWant className="w-6" />);

how to import css file in react when generating static html and inject imported css into html head tag?

I am trying to generate static html from react using renderToStaticMarkup method. The problem I am facing right now is that I am not able to import css into react component. I want to import css in my React components like css-modules (import styles from './style.css'). And then inject that loaded css into generated static html head. How can I accomplish that?
P.S. I can't use webpack due to some constraints. If there is any babel plugin availabe for this specific case, then please let me know.
Here is how I am generating static html from react component:
const reactElement = require('react').createElement;
const ReactDomServer = require('react-dom/server');
const renderHTML = Component => {
return ReactDomServer.renderToString(reactElement(Component))
}
You can pass a URL in as a prop and render a <link/> tag. Made an example here, not sure if that would meet your needs or if you need it to be a style tag.
This may be challenging without a lot of custom logic.
If you want to inline the CSS only for the initial render and then fetch the rest after the initial render, styled-components may be a better option because it supports exactly what you're trying to achieve without too much configuration: https://www.styled-components.com/docs/advanced#server-side-rendering
May be I am too late you can also create It like this way.
React.createElement("style", {},[ "body {background-color: powderblue;}
h1 {color: blue;}
p {color: red;}" ])
Output:
<style>
body {background-color: powderblue;}
h1 {color: blue;}
p {color: red;}
</style>
Since createElement take 3 params and last one is children we can put our vanila css inside it as a children. You can put any imported file in the form of string and it will convert to style tag

React - pass props/state to rendered HTML string?

In my React app, I have stored a text template as an HTML string on the server which is passed up to the client to be rendered. Is it possible to render this HTML string in such a way that it can access it's parent component state/props? I've tried using dangerouslySetInnerHTML and ReactDOMServer.renderToString(), both of which render the string as HTML, but render {props.text} as a literal string, rather than a reference to the component's props.
For example, on the server I have stored:
<div>Welcome to my app</div>
<div>{props.message}</div>
<div>Have fun</div>
And the component
import React from "react"
import { connect } from "react-redux"
const mapStateToProps = (state) => {
return { state }
},
WelomeBody = (props) => {
return (
<div dangerouslySetInnerHTML={{"__html": props.state.welcomeMessageBody}} />
)
}
export default connect(mapStateToProps, null)(WelomeBody)
but this results in:
Welcome to my app
{props.message}
Have fun
Is there a way that this can be rendered so as to access the value of props.message, rather than just rendering it literally?
If what you have in mind is to send down a React component (in JSX syntax) from your server to your client and have the client somehow rehydrate/compile it into an actual, working component, this is not achievable that easily.
When you build and bundle your React app, only components that are statically referenced/imported in your application at compile time can be used at runtime on the browser.
So, in order to dynamically render a component from a template and embed variables into it, your choices are:
Render it into final HTML on your server, send down that HTML and have a simple React component perform dangerouslySetInnerHTML for it. But, like you've already observed, that content has to be the full HTML code, no templates, variables, JSX, etc. Everything is string and HTML at this point, no more processing.
Alternatively, send down a Markdown document and have a Markdown component parse it into HTML and display it.
Create a sophisticated component that can receive a string, parse it, tokenize it, substitute values in it, etc. Essentially, create your own template-processing component that can read a template (the string sent down from your server) and embed the appropriate values into it.
A simple solution (to substitute values into a template) would be:
function Filler(props) {
let html = props.template;
Object.keys(props).forEach(key => {
if (key !== "template")
html = html.replace("{" + key + "}", props[key]);
});
return <div dangerouslySetInnerHTML={{__html: html}} />;
}
and use it like
<Filler template={"<h1>Hello, {firstname} {lastname}</h1>"} firstname="John" lastname="Doe" />
But again, this is far from a full-fledged actual React component.

Rendering React components from text

I'm working on a simple blog/CMS tool. In authoring content, I'm supporting an option to enter raw html/css. The user enters this content into a text area, and I can render this into a page using dangerouslySetInnerHtml. That works fine.
However, I'd really like to embed some React components the content as well. Ideally I'd like to enter something like this into a textarea...
<div>
<p>Some content</p>
<MyPictureComponent url="..." />
</div>
...and then render that into a page and have it create the MyPictureComponent.
I'll be storing the above "code" in a database, and rendering it dynamically as users view the "post". Is it possible to rendering that raw text as functioning React?
I saw this project (HTML to React), which seemed promising, bu that seems to only parse the HTML given to it, not tags for React components.
I found a way to do what I want, with the caveat that it's somewhat manual, and potentially dangerous. However, in my case, I'm creating a blog/CMS for a very limited audience, and the concern about users potentially inserting harmful content is non-existent.
My approach ended up using html-to-react (https://www.npmjs.com/package/html-to-react). Html-to-react accepts a string (containing raw HTML markup), and transforms it into a proper React component. By default, its parse() method doesn't properly handle React components (it just turns them into lower-case-named html elements). However, the library has a parseWithInstructions, which allows you to control how individual nodes in the component are rendered.
In my case, I want to enable certain React components to be rendered. One of those is my ExternalLink component. What follows is the method I use to transform some user-entered raw HTML into a React component that properly rendered my components.
updatePreview() {
// Combine the user-entered CSS and the user-entered HTML into a single string.
let outputPreview = "<div><style>" + this.state.cssValue + "</style><div>" + this.state.inputValue + "</div></div>";
let htmlToReactParser = new HtmlToReact.Parser();
let processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions(React);
let processingInstructions = [
{
// Custom <ExternalLink> processing
shouldProcessNode: function (node) {
return node.name === 'externallink';
},
processNode: function (node, children) {
let attribs = node.attribs;
return <ExternalLink {...attribs}>{children}</ExternalLink>
}
},
{
// Anything else
shouldProcessNode: function (node) {
return true;
},
processNode: processNodeDefinitions.processDefaultNode
}];
// Convert the HTML into a React component
let reactComponent = htmlToReactParser.parseWithInstructions(outputPreview, () => true,
processingInstructions);
// Now that we have a react component, we set it to the state.
// Our render() method includes a "{this.state.outputPreview}", which causes the
// component to be rendered.
this.setState({outputPreview: reactComponent, refreshPreviewTimer: null});
}
Note that outputString in the first line of the method will contain some raw text like this:
"<div>
<style></style>
<div>
<p>Here's a link:<p>
<ExternalLink url="http://www.google.com">Google</ExternalLink>
</div>
</div>"
There are some approaches I'll take to generalize this approach more, using a dictionary of strings to enable support for a wider range of Components. I'll also look at some approach to automatically importing the desired Component. (Currently, I'm manually importing all supported components.)
So, all credit goes to the author of html-to-react, though I may encourage him to include an example of rendering child components.

Resources