this.props.children selecting innerHTML - reactjs

Lets say our component structure is as follows:
CommentList
Comment
CommentList is defined as:
var CommentList = React.createClass({
render: function() {
return (
<div className="commentList">
<Comment author="Pete Hunt">This is one comment</Comment>
</div>
);
}
});
and Comment defined as
var Comment = React.createClass({
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
{this.props.children}
</div>
);
}
});
It seems a bit strange that this.props.children is selecting the text "This is one comment". Why is it called "children", shouldn't it be "innerHTML" or something? I feel like I'm missing something.

What you wrap between a component, is not its inner HTML, but is wrapped into a ReactElement, and passed as a prop in props.children.
When you use JSX syntax, whatever is inserted inside the tags of an element, becomes the element 0 of the props.children array of the component. When there is just one child, no array is created and props.children points to the only element. From the docs:
However, when there is only a single child, this.props.children will
be the single child component itself without the array wrapper. This
saves an array allocation.

With react you deal with a virtual DOM that's why you're not interacting directly with the DOM but with the virtual DOM.
this.props.children designates the children being passed onto you by the owner.
https://facebook.github.io/react/tips/children-undefined.html
So here the child will get "This is one comment" probably inside a span (automatically added). Because it's passed by the owner component.
This way you interact with the virtual DOM and change the content of your component.
Hope it helps

Related

We simply cannot write `<Foo style={ } ... />` in ReactJS?

I got a little carried away and wrote
<Foo
style={ }
...
/>
in the old days, we actually had Foo as a <div> usually, so that could make sense, to actually impose that style on the <div>. But later on, we actually had <> ... </> and it'd be not good to have ReactJS impose an element (such as <div>) containing all those elements, so the style line doesn't make sense in this case.
So in a way, we never could treat a component as an element and give it some basic HTML attributes? How would we set HTML attribute for a component?
We could pass all HTML attributes to a React component as a prop and then inside the component's render function assign those props to the actual DOM HTML element.
const Foo = ({style}) => (
<div style={style}> // assigning inline style to HTML dom element
This is Foo
</div>
)
<Foo style={color: 'red'} /> // passing style as a prop to Foo react component
You can use JSX spread attributes and make it more flexible so you won't have to specify individual HTML attributes as a prop. You simply spread restProps over your HTML element.
const Foo({ name, ...restProps }) => {
return <div {...restProps}>{name}</div>;
}
Now you can pass DOM attributes to Foo and they'll be passed through to div.
<Foo
name="This is Foo"
className="foo-class"
id="my-foo"
style="color: red"
/>
When you use <> ... </> this is a shorthand of <React.Fragment>...</React.Fragment> which lets you only group multiple list of children WITHOUT adding extra node to the DOM when it gets rendered.
return (
<>
<Foo />
<Foo />
<Foo />
</>
);
This will render as
<div>This is Foo</div>
<div>This is Foo</div>
<div>This is Foo</div>
so if you find yourself need to pass a HTML attribute to <>...</> you can simply change react fragment to a <div>...</div> or <span>...</span> or other proper HTML elements and set your attribute on.

React - component will not render in foreach loop?

I have a small application when you open a tab the React Route redirects to the given component (container). And from the container i want to render the child components. I use the foreach loop to go through a list that has the props that need to be given to the child. However, the child doesn't get rendered.
I don't want to use map because i use libraries that depend on the list class.
render() {
return (
<div>
{this.state.list.ForEach((element) =>
<ChildComponent childName={element.name}
}
</div>
);
}
}
You are confusing Array.forEach with Array.map. forEach does not return anything. The correct way is:
<div>
{this.state.list.map((element, index) =>
<ChildComponent key={index} childName={element.name} />
)}
</div>
map converts the given element into another element, a component in this case. The result of calling map is an array of components that is then rendered. forEach always returns undefined therefore the result of your code is the same as writing:
<div>
{undefined}
</div>
Note that key is also necessary when rendering lists of components.

How can I render a <template /> tag with react?

Sometimes you might need to render web-components from your react app.
Web-components often use a special <template> ... </template> tag.
But if I try to render such markup with react like this:
render() {
return (
<template>
<div>some content</div>
</template>
)
}
then my web-components don't work correctly.
The reason is that JSX does a different job than what the <template /> tags exists for. The idea of a template tag is to not render its children and pretty much handle it like unparsed text (the browser actually parses it just to make sure its valid html, but does nothing more)
But when you write this in JSX:
return (
<template>
<div>some content</div>
</template>
)
you're basically instructing react to create a 'template' element and then create a 'div' element and then to append this div to the template as a child.
So under hood this happens:
const template = document.createElement('template')
const div = document.createElement('div')
const text = document.createTextNode('some text')
div.appendChild(text)
template.appendChild(div)
But what you want is to set the contents of the <template /> as a string. You can use innerHTML for that.
Solution
One solution would be:
render() {
return (
<template
dangerouslySetInnerHTML={{
__html: '<div>some content</div>'
}}
/>
)
}
Now you're asking react to create all those children tags as node elements but letting the browser decide what to do with them.
Nicer solution
You might not want to use dangerouslySetInnerHTML all the time. So let's create a helper component:
function Template({ children, ...attrs }) {
return (
<template
{...attrs}
dangerouslySetInnerHTML={{ __html: children }}
/>
);
}
Now any time you need to use a template you can use it like this:
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
Don't forget to put the inner content in quotes, because it should be a string.
I know that this question has already an answer, but there is another, I guess simpler solution to do that creating hoc (Higher Order Component).
Just create new "component" like this:
// hoc/Template.js
const template = props => props.children
export default template
and then you can use it in your project this way:
import './hoc/Template.js'
...
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
Newer version of react has already build it such a component, so you can achieve same thing without creating component.
import { Fragment } from 'react'
...
render() {
return (
<Fragment>
{'<div>some content</div>'}
</Fragment>
)
}
This is actually a bug in React! https://github.com/facebook/react/issues/19932
When the native browser parses the <template> tag, it appends all children to the template's content fragment, instead of adding them as children. So a template should never actually have any children.
However, if you programmatically build the DOM (the way react-dom does), then the template will have an empty content fragment, since all children are added as children. This is why web components will not behave properly with these templates.
Here's an example of the problem too: https://codesandbox.io/s/new-sun-8l62o?file=/src/App.tsx:1056-1118
The easiest workaround is to use dangerouslySetInnerHTML, like so:
<template dangerouslySetInnerHTML={{ __html: `
<p> Some text </p>
` }} />
This is limited by the fact that you can only supply raw HTML (no custom React elements). However, since you're using the <template> tag in the first place, it seems highly unlikely that you'd also be using React within the template.

When should I be using React.cloneElement vs this.props.children?

I am still a noob at React and in many examples on the internet, I see this variation in rendering child elements which I find confusing. Normally I see this:
class Users extends React.Component {
render() {
return (
<div>
<h2>Users</h2>
{this.props.children}
</div>
)
}
}
But then I see an example like this:
<ReactCSSTransitionGroup
component="div"
transitionName="example"
transitionEnterTimeout={500}
transitionLeaveTimeout={500}
>
{React.cloneElement(this.props.children, {
key: this.props.location.pathname
})}
</ReactCSSTransitionGroup>
Now I understand the api but the docs don't exactly make clear when I should be using it.
So what does one do which the other can't? Could someone explain this to me with better examples?
props.children isn't the actual children; It is the descriptor of the children. So you don't have actually anything to change; you can't change any props, or edit any functionality; you can only read from it. If you need to make any modifications you have to create new elements using React.CloneElement.
https://egghead.io/lessons/react-use-react-cloneelement-to-extend-functionality-of-children-components
An example:
main render function of a component such as App.js:
render() {
return(
<Paragraph>
<Sentence>First</Sentence>
<Sentence>Second</Sentence>
<Sentence>Third</Sentence>
</Paragraph>
)
}
now let's say you need to add an onClick to each child of Paragraph; so in your Paragraph.js you can do:
render() {
return (
<div>
{React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
onClick: this.props.onClick })
})}
</div>
)
}
then simply you can do this:
render() {
return(
<Paragraph onClick={this.onClick}>
<Sentence>First</Sentence>
<Sentence>Second</Sentence>
<Sentence>Third</Sentence>
</Paragraph>
)
}
Note: the React.Children.map function will only see the top level elements, it does not see any of the things that those elements render; meaning that you are providing the direct props to children (here the <Sentence /> elements). If you need the props to be passed down further, let's say you will have a <div></div> inside one of the <Sentence /> elements that wants to use the onClick prop then in that case you can use the Context API to do it. Make the Paragraph the provider and the Sentence elements as consumer.
Edit:
Look at Vennesa's answer instead, which is a better explanation.
Original:
First of all, the React.cloneElement example only works if your child is a single React element.
For almost everything {this.props.children} is the one you want.
Cloning is useful in some more advanced scenarios, where a parent sends in an element and the child component needs to change some props on that element or add things like ref for accessing the actual DOM element.
In the example above, the parent which gives the child does not know about the key requirement for the component, therefore it creates a copy of the element it is given and adds a key based on some unique identifier in the object. For more info on what key does: https://facebook.github.io/react/docs/multiple-components.html
In fact, React.cloneElement is not strictly associated with this.props.children.
It's useful whenever you need to clone react elements(PropTypes.element) to add/override props, without wanting the parent to have knowledge about those component internals(e.g, attaching event handlers or assigning key/ref attributes).
Also react elements are immutable.
React.cloneElement( element, [props], [...children] ) is almost equivalent to:
<element.type {...element.props} {...props}>{children}</element.type>
However, the children prop in React is especially used for containment (aka composition), pairing with React.Children API and React.cloneElement, component that uses props.children can handle more logic(e.g., state transitions, events, DOM measurements etc) internally while yielding the rendering part to wherever it's used, React Router <switch/> or compound component <select/> are some great examples.
One last thing that worth mentioning is that react elements are not restricted to props.children.
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
They can be whatever props that makes sense, the key was to define a good contract for the component, so that the consumers of it can be decoupled from the underlying implementation details, regardless whether it's using React.Children, React.cloneElement, or even React.createContext.

React - How to pass HTML tags in props?

I want to be able to pass text with HTML tags, like so:
<MyComponent text="This is <strong>not</strong> working." />
But inside of MyComponent's render method, when I print out this.props.text, it literally prints out everything:
This is <strong>not</strong> working.
Is there some way to make React parse HTML and dump it out properly?
You can use mixed arrays with strings and JSX elements (see the docs here):
<MyComponent text={["This is ", <strong>not</strong>, "working."]} />
There's a fiddle here that shows it working: http://jsfiddle.net/7s7dee6L/
Also, as a last resort, you always have the ability to insert raw HTML but be careful because that can open you up to a cross-site scripting (XSS) attack if aren't sanitizing the property values.
Actually, there are multiple ways to go with that.
You want to use JSX inside your props
You can simply use {} to cause JSX to parse the parameter. The only limitation is the same as for every JSX element: It must return only one root element.
myProp={<div><SomeComponent>Some String</div>}
The best readable way to go for this is to create a function renderMyProp that will return JSX components (just like the standard render function) and then simply call myProp={ this.renderMyProp() }
You want to pass only HTML as a string
By default, JSX doesn't let you render raw HTML from string values. However, there is a way to make it do that:
myProp="<div>This is some html</div>"
Then in your component you can use it like that:
<div dangerouslySetInnerHTML=myProp={{ __html: this.renderMyProp() }}></div>
Beware that this solution 'can' open on cross-site scripting forgeries attacks. Also beware that you can only render simple HTML, no JSX tag or component or other fancy things.
The array way
In react, you can pass an array of JSX elements.
That means:
myProp={["This is html", <span>Some other</span>, "and again some other"]}
I wouldn't recommend this method because:
It will create a warning (missing keys)
It's not readable
It's not really the JSX way, it's more a hack than an intended design.
The children way
Adding it for the sake of completeness but in react, you can also get all children that are 'inside' your component.
So if I take the following code:
<SomeComponent>
<div>Some content</div>
<div>Some content</div>
</SomeComponent>
Then the two divs will be available as this.props.children in SomeComponent and can be rendered with the standard {} syntax.
This solution is perfect when you have only one HTML content to pass to your Component (Imagine a Popin component that only takes the content of the Popin as children).
However, if you have multiple contents, you can't use children (or you need at least to combine it with another solution here)
From React v16.02 you can use a Fragment.
<MyComponent text={<Fragment>This is an <strong>HTML</strong> string.</Fragment>} />
More info: https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html
You can use dangerouslySetInnerHTML
Just send the html as a normal string
<MyComponent text="This is <strong>not</strong> working." />
And render in in the JSX code like this:
<h2 className="header-title-right wow fadeInRight"
dangerouslySetInnerHTML={{__html: props.text}} />
Just be careful if you are rendering data entered by the user. You can be victim of a XSS attack
Here's the documentation:
https://facebook.github.io/react/tips/dangerously-set-inner-html.html
You can use the <></> Fragments to pass the HTML in the props.
<MyComponent text={<>"This is <strong>not</strong> working."</>} />
Reference: https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html#jsx-fragment-syntax
<MyComponent text={<span>This is <strong>not</strong> working.</span>} />
and then in your component you can do prop checking like so:
import React from 'react';
export default class MyComponent extends React.Component {
static get propTypes() {
return {
text: React.PropTypes.object, // if you always want react components
text: React.PropTypes.any, // if you want both text or react components
}
}
}
Make sure you choose only one prop type.
On a client-side react application, there are a couple of ways of rendering a prop as a string with some html. One safer than the other...
1 - Define the prop as jsx (my preference)
const someProps = {
greeting: {<div>Hello${name_profile}</div>}
}
const GreetingComopnent = props => (
<p>{props.someProps.greeting}</p>
)
• The only requirement here is that whatever file is generating this prop needs to include React as a dependency (in case you're generating the prop's jsx in a helper file etc).
2 - Dangerously set the innerHtml
const someProps = {
greeting: '<React.Fragment>Hello${name_profile}</React.Fragment>'
}
const GreetingComponent = props => {
const innerHtml = { __html: props.someProps.greeting }
return <p dangerouslySetInnerHtml={innerHtml}></p>
}
• This second approach is discouraged. Imagine an input field whose input value is rendered as a prop in this component. A user could enter a script tag in the input and the component that renders this input would execute this potentially malicious code. As such, this approach has the potential to introduce cross-site scripting vulnerabilities.
For more information, refer to the official React docs
For me It worked by passing html tag in props children
<MyComponent>This is <strong>not</strong> working.</MyComponent>
var MyComponent = React.createClass({
render: function() {
return (
<div>this.props.children</div>
);
},
Set the text prop type to any and do this:
<MyComponent text={
<React.Fragment>
<div> Hello, World!</div>
</React.Fragment>
}
/>
Example
You can do it in 2 ways that I am aware of.
1- <MyComponent text={<p>This is <strong>not</strong> working.</p>} />
And then do this
class MyComponent extends React.Component {
render () {
return (<div>{this.props.text}</div>)
}
}
Or second approach do it like this
2- <MyComponent><p>This is <strong>not</strong> working.</p><MyComponent/>
And then do this
class MyComponent extends React.Component {
render () {
return (<div>{this.props.children}</div>)
}
}
You can successfully utilize React fragments for this task. Depending on the React version you use, you can use short syntax: <> or the full tag: <React.Fragment>. Works especially well if you don't want to wrap entire string within HTML tags.
<MyComponent text={<>Hello World. <u>Don't be so ruthless</u>.</>} />
Parser from html-react-parser is a good solution. You just have to
install it with npm or yarn
import Parser from 'html-react-parser';
call it with :
<MyComponent text=Parser("This is <strong>not</strong> working.") />
and it works well.
Do like this:
const MyText = () => {
return (
<>
This is <strong>Now</strong> working.
</>
)
}
then pass it as a props as:
<MyComponent Text={MyText} />
now you can use it in your component:
const MyComponent = ({Text}) => {
return (
<>
// your code
{<Text />}
// some more code
</>
)
}
#matagus answer is fine for me, Hope below snippet is helped those who wish to use a variable inside.
const myVar = 'not';
<MyComponent text={["This is ", <strong>{`${myVar}`}</strong>, "working."]} />
In my project I had to pass dynamic html snippet from variable and render it inside component. So i did the following.
defaultSelection : {
innerHtml: {__html: '<strong>some text</strong>'}
}
defaultSelection object is passed to component from .js file
<HtmlSnippet innerHtml={defaultSelection.innerHtml} />
HtmlSnippet component
var HtmlSnippet = React.createClass({
render: function() {
return (
<span dangerouslySetInnerHTML={this.props.innerHtml}></span>
);
}
});
Plunkr example
react doc for dangerouslySetInnerHTML
You could also use a function on the component to pass along jsx to through props. like:
var MyComponent = React.createClass({
render: function() {
return (
<OtherComponent
body={this.body}
/>
);
},
body() {
return(
<p>This is <strong>now</strong> working.<p>
);
}
});
var OtherComponent = React.createClass({
propTypes: {
body: React.PropTypes.func
},
render: function() {
return (
<section>
{this.props.body()}
</section>
);
},
});
Yes, you can it by using mix array with strings and JSX elements. reference
<MyComponent text={["This is ", <strong>not</strong>, "working."]} />
Adding to the answer: If you intend to parse and you are already in JSX but have an object with nested properties, a very elegant way is to use parentheses in order to force JSX parsing:
const TestPage = () => (
<Fragment>
<MyComponent property={
{
html: (
<p>This is a <a href='#'>test</a> text!</p>
)
}}>
</MyComponent>
</Fragment>
);
This question has already a lot of answers, but I had was doing something wrong related to this and I think is worth sharing:
I had something like this:
export default function Features() {
return (
<Section message={<p>This is <strong>working</strong>.</p>} />
}
}
but the massage was longer than that, so I tried using something like this:
const message = () => <p>This longer message is <strong>not</strong> working.</p>;
export default function Features() {
return (
<Section message={message} />
}
}
It took me a while to realize that I was missing the () in the function call.
Not working
<Section message={message} />
Working
<Section message={message()} />
maybe this helps you, as it did to me!
We can do the same thing in such a way.
const Child = () => {
return (
write your whole HTML here.
)
}
now you want to send this HTML inside another component which name is Parent component.
Calling :-
<Parent child={<child/>} >
</Parent>
Use Of Child:-
const Parent = (props) => {
const { child } = props;
return (
{child}
)
}
this work perfect for me.
Here is a solution that doesn't use the dangerouslySetInnerHTML which is dangerous as the name says.
import { IntlProvider, FormattedMessage } from "react-intl";
<FormattedMessage
id="app.greeting"
description="Bold text example"
defaultMessage="Look here, I can include HTML tags in plain string and render them as HTML: <b>Bold</b>, <i>Italics</i> and <a>links too</a>."
values={{
b: (chunks) => <b>{chunks}</b>,
i: (chunks) => <i>{chunks}</i>,
a: (chunks) => (
<a class="external_link" target="_blank" href="https://jiga.dev/">
{chunks}
</a>
)
}}
/>
This should be rendered as:
Full example in https://jiga.dev/react-render-string-with-html-tags-from-props/
This works for me
<MyComponent text={<>some text <strong>bold text</strong> and more.</>} />
Also if you want to pass variable here, you can try like this
<MyComponent text={<>My name is {variableName}. <strong>Bold text</strong> normal text</>} />
Have appended the html in componentDidMount using jQuery append. This should solve the problem.
var MyComponent = React.createClass({
render: function() {
return (
<div>
</div>
);
},
componentDidMount() {
$(ReactDOM.findDOMNode(this)).append(this.props.text);
}
});

Resources