How to convert props.children to string in react js - javascript

I want to convert react children to string.
In the below example I am passing div, h2 and p as children to Example in Exampleclass. when I call props.children in Example it returns an array but I need a props.children in the string format so that I can use it in dangerouslySetInnerHTML.
import React, { Component } from "react";
import Example from "./example";
export default class Exampleclass extends Component {
render() {
return (
<div>
<p>in class</p>
<Example> // passing div, h2 and p as children to Example
<div>
<h2>function</h2>
<p>function</p>
</div>
</Example>
</div>
);
}
}
import React from "react";
export default function example(props) {
console.log("child", props.children); // returns array but i need <div><h2>function</h2><p>function</p></div> here
return <div dangerouslySetInnerHTML={{ __html: `${props.children}` }} />; // returns [object Object]
}

It would be difficult to transform children object into some kind of a flat string with a JSX-a-like structure. children is a complex object with a lot of nested fields. You would have to convert it firstly to an array, then iterate over it, probably recursively, pick correct props and so on.
However you could pass your children not as a children, but as a separate prop.
const Child = ({ child }) => {
return <div dangerouslySetInnerHTML={{ __html: child }} />;
};
const App = () => {
const data = `
<div>
<h2>function</h2>
<p>function</p>
</div>
`;
return <Child child={data} />
};
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Edit: You could, however, use renderToStaticMarkup function, but it will render only pure html (it will compile components into html). In your example you are passing only html elements so I guess it would be okey.
const markup = ReactDOMServer.renderToStaticMarkup(children);
return <div dangerouslySetInnerHTML={{ __html: markup }} />;

react-children-utilities has a function, onlyText, that does this.
It uses React.Children.toArray(children).reduce then recursively loops through children, concatenating a string made up of every child and nested child that is a string or a number, skipping everything else.
Disclaimer: be aware that if your component's children includes components with props or attributes that behave like children when rendered, but aren't actually in the React elements as children, they'll be omitted.
For example, if you're trying to approximate the text that tools like screen readers or search engine bots would read on focusing an element, this approach will skip text in attributes like alt, label or value that such tools might include. It also won't do things like collapse whitespace.
Since onlyText is a pretty simple function, I'd suggest looking at its implementation (linked above) and consider writing your own variant that adds in any other relevant attributes that your use case needs which would otherwise be skipped.

Related

Turning a TSX/JSX React into a string or DOM element

So I have an unusual requirement from my tech lead. I essentially need to turn a callback that returns JSX and turn the output into an HTMLElement or a string.
The thing is that we're building a reusable, low level infinite scroll component that reuses the DOM so that only 20 DOM elements are ever present on the componenent. Here is the codepen for the PoC I was sent for this:
https://codepen.io/hemant30/pen/rNWQEZy?editors=0010
The codepen works, but doesn't handle anything in React, it's purely DOM manipulation.
The callback the component receives is something like this:
(data: any) => {
return (
<div key={data.id}>
{data.id} - {data.name}
<br />
Text
</div>
);
}
The idea is to be able to then, when using this callback internally, turn it into something that can be added to an element through dangerouslySetInnerHTML.
I have tried multiple things and approaches, but my approaches haven't been approved. So I turn to you to see if there is a way to do this or not.
You can check renderToStaticMarkup method, it can transform jsx into string html.
import ReactDOMServer from 'react-dom/server'
const htmlString = ReactDOMServer.renderToStaticMarkup(
<div>
<Component/>
/* etc. */
</div>
);

reactjs : Diffrence between regular/arrow functions and react functional components

What difference you see between regular/arrow functions and react functional component
React functional component
import React from 'react';
function MyComp(){
return <span>test</span>
}
Regular function
function myFun(){
return null;
}
Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called props) and return React elements describing what should appear on the screen.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
From a JavaScript perspective, this is nothing but a function, except for a special return. It's JSX and each JSX element is just syntactic sugar for calling React.createElement(component, props, ...children).
Basically, you can replace the JSX in the example above with something like:
React.createElement('h1', null, `Hello ${props.name}`);
And this is nothing but a JavaScript :)
And then you can render your component:
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
You call ReactDOM.render() with the <Welcome name="Sara" /> element.
React calls the Welcome component with {name: 'Sara'} as the props.
Our Welcome component returns a <h1>Hello, Sara</h1> element as the result.
React DOM efficiently updates the DOM to match <h1>Hello, Sara</h1> and you get it on the screen.
There is one important requirement for React functional component!
Always start component names with a capital letter.
React treats components starting with lowercase letters as DOM tags. For example, represents an HTML div tag, but represents a component and requires Welcome to be in scope.
This function from your example can be used as a functional component:
function MyFun() { // name should start with a capital
return null;
}
React won't display anything because React does not render if component returns null.

Making an HTML string from a React component in background, how to use the string by dangerouslySetInnerHTML in another React component

I'm trying to render LaTeX strings in a React project.
Although I use the react-mathjax React components, I want to get an HTML string made from the LaTeX strings in order to concatenate it and the other strings and set it by dangerouslySetInnerHTML.
My current code I tried
Sample cod here
LaTeX strings are given as strings
Make an empty DOM aDom by document.createElement('span') (in background. not in the document DOM tree.)
Render a LaTeX string by ReactDOM.render into aDom
After rendering, get a string by aDom.innerHTML or .outerHTML
Problem
The value of aDom.innerHTML (or .outerHTML) is "<span><span data-reactroot=\"\"></span></span>" (almost empty)
although aDom has a perfect tree that MathJax generated.
Briefly,
aDom: 🙆
aDom.outerHTML: 🙅
Question
How can I get the 'correct' HTML string from aDom above?
This seems to work just fine if you want to render any component to a HTML string:
import { renderToStaticMarkup } from 'react-dom/server'
function componentToString() {
return renderToStaticMarkup(<MyAwesomeComponent some="props" or="whatever" />)
}
From what I see, you are getting what you'd expect to get.
Given a root element (aDom in your case), ReactDOM will render it's root component inside this element, and this component's element will have the attribute data-reactroot.
So what you are seeing is exactly how it should be. From what I've tested, the inner dom tree should be there as well.
var Another = React.createClass({
render: function() {
return (
<div>Just to see if other components are rendered as well</div>
);
}
});
var Hello = React.createClass({
render: function() {
return (
<div id="first">
<div id="sec-1">Hello</div>
<div id="sec-2">{ this.props.name }</div>
<Another />
</div>
);
}
});
var a = document.createElement('div');
ReactDOM.render(
<Hello name = "World" /> ,
a
);
console.log(a.outerHTML);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
The result in the console is:
<div><div data-reactroot="" id="first"><div id="sec-1">Hello</div><div id="sec-2">World</div><div>Just to see if other components are rendered as well</div></div></div>
I recommend using renderToStaticMarkup over renderToString as it does not add any extra DOM attributes that React uses internally, like `data-reactroot:
import { renderToStaticMarkup } from 'react-dom/server'
getString() {
return renderToStaticMarkup(<AnyComponent />)
}
Documentation:

Render multiple React components into a single DOM element

I'm looking to render multiple modals into a single ReactDOM element. Here's the HTML structure that React renders to.
<body>
<div id="modal-socket"></div> // Insert multiple here
<div id="wrapper">
// Other content goes here
</div>
</body>
There's a long story behind why I need to render multiple components into #modal-socket but I want to do something akin to this:
ReactDOM.render(<AddMeasurableModal />, document.getElementById("modal-socket"));
ReactDOM.render(<AddMeasurableModal />, document.getElementById("modal-socket"));
ReactDOM.render(<AddMeasurableModal />, document.getElementById("modal-socket"));
Obviously this replaces the current content of #modal-socket on each render call.. So I don't get my end result. Boo.
Did a search and found a few answers on it but none meet my needs.
Cheers.
As you told in a comment, the dynamic way would be something like this
Inside of a main component you could do:
Imagine having an array like:
let myArray = [
{
prop1: 'hello world'
},
{
prop1: 'Hey there!'
}
]
//Then in the render function (you can put that array into the state or something)
render(){
return (
<div>
{myArray.map((entry,index) => {
return <AddMeasurableModal key={index} {...entry} />
})}
</div>
)
}
this will create as many AddMeasurableModal components as there are entrys in the myArray variable and add every property stored as props onto the component (In this case, every AddMeasurableModal component has access to the this.props.prop1 value, because of the {...entry} spread syntax)
Notice how I only put myArray.map() into the render function inside of {}?
React renders every array of components without further configuration inside of the render function. And Array.map() returns an array. Just make sure to return only valid react elements! When doing this, don't forget to add a uniqe key prop to each element to avoid warnings.
EDIT: in this case, the key prop is the current index in the array, but when fetching data from a server I would recommend to use a uniqe id from the database or something to avoid rendering bugs.
If you don't want to map over an array, you can just set a number of components and then loop over them, creating an array of components and put them into the render function.
Wrap your multiple modals into 1 container and render that, eg:
let modals = (
<div>
<AddMeasurableModal />
<AddMeasurableModal />
<AddMeasurableModal />
</div>
);
ReactDOM.render(modals, document.getElementById("modal-socket"));

How to pass optional elements to a component as a prop in reactjs

I am trying to figure out the proper "react" way to pass in an optional prop that is an Element to a container component, that is handled differently from the children of that component.
For a simple example, I have a Panel component, which renders its children, that also has an optional "title" prop (which is an element rather than a string, for the sake of the example) that gets specially rendered (put in a special spot, with special behaviors in while maintaining the abstraction.
One option is to have a component which is pulled out of the children and rendered specially:
<Panel>
<Title> some stuff</Title>
<div> some other stuff</div>
</Panel>
But it seems wierd to have the children pulled out and handled separately like that.
How is this normally handled in react, and am I even thinking about this the right way
You don't need to do anything special. Just pass the title component as a prop, and then use {this.props.title} wherever you want it to be rendered:
class Panel extends React.Component {
render() {
return <div>
{this.props.title}
<div>Some other stuff...</div>
</div>;
}
}
class App extends React.Component {
render() {
var title = <Title>My Title</Title>;
return <Panel title={title}/>;
}
}
If you don't pass any value for the title prop (or if the value is false, null, or undefined) then nothing will be rendered there.
This is a fairly common pattern in React.
you can do something like this
render(){
<div>
{this.props.title ? this.props.title : null}
{this.props.children}
</div>
}
basically if you pass a title element as a prop then create it as an element and render it. else just put in null...
to create it you would do something like this.
<Panel title={<Title>Something Here</Title>}
<div> something here</div>
</Panel>
This is generally how react should handle optional child components
When you need an attribute from your optional prop, you will have to check first if the prop was delivered. Otherwise, you will get a:
TypeError: Cannot read property 'yourPropProperty' of undefined
In conditional rendering context (depending on my optional this.props.ignore array), this won't work:
{!this.props.ignore.includes('div')) && (
<div>
Hey
</div>
)}
Instead, you should do:
{(!this.props.ignore || !this.props.ignore.includes('div'))) && (
<div>
Hey
</div>
)}
One thing you can do is have default props (usually initialised to a no-op) for your component.
For example, if you want to have an optional function prop:
class NewComponent extends React.Component {
...
componentDidMount() {
this.props.functionThatDoesSomething()
}
}
NewComponent.defaultProps = {
functionThatDoesSomething: () => {}
}
This way, parent components can choose to pass the function prop or not and your app won't crash due to the error
this.props.functionThatDoesSomething is not a function
.

Categories