Dynamically changing React component tag - reactjs

I'm creating a Landing page for a project and want to reduce my code by making a helper function to display my four different paper components. Everything seems to be working correctly except for displaying my Icon components that are within the papers.
When I console log Icon it is the correct text, yet the component doesn't appear on the page and I receive these two warnings for each component tag:
"Warning: The tag <CardTravelIcon> is unrecognized in this browser. If you
meant to render a React component, start its name with an uppercase letter."
and
"Warning: <CardTravelIcon /> is using uppercase HTML. Always use lowercase
HTML tags in React."
If I just hard code in CardTravelIcon or any of the other 3 component names in that exact format instead of using Icon from my map function, everything works as expected. Below is the code for my helper function:
class Landing extends Component {
renderPapers() {
const classes = this.props.classes;
return _.map(infoPapers, ({ description, Icon }) => {
return (
<Grid item xs={6} sm={3} key={Icon}>
<Paper className={classes.paper}>
<Icon className={classes.paperIcons} />
{description}
</Paper>
</Grid>
);
});
}
I'm at a loss and would appreciate any help. Thank you.

So you want to pass a component as a variable, right?
Let's say you have this minimal CardTravelIcon component:
const CardTravelIcon = ( props ) => (
<div className={ props.className }>Card Travel Icon</div>
);
And infoPapers data like this (Icon is a reference to the component):
const infoPapers = [
{
description: "Paper 1 description",
Icon : CardTravelIcon
}];
You didn't show us the infoPapers data, but I suspect you're trying to pass a string as the component, e.g. { Icon : "<CardTravelIcon />" } and expect it to work like setting innerHTML (rendering HTML from a string). This is not the case in React, the JSX code needs to be transpiled into calls to React.CreateElement first, and it isn't done by parsing strings.
If you pass references to components everything should be rendered just fine with the following render method (note: removed lodash in favor of native map method, for clarity):
class Landing extends React.Component {
render() {
return infoPapers.map(({ description, Icon }, idx) => {
return (
<div key={ idx }>
<Icon />
{description}
</div>
);
});
}
};
Here's a working example: https://jsfiddle.net/svygw338/

Related

Is it possible to render a react class when given its name as a string?

Here's a smaller example of what I'm trying to do, I don't know if it's possible to do something similar or I should use an entirely different method.
import {Design1, Design2} from './page-designs';
let designs = {
"page1":"Design1",
"page2":"Design2",
"page3":"Design1",
"page4":"Design2"
}
class DesignedPage extends React.Component {
let Design = designs[this.props.page]
render(){
return(
<div className="row flex-fill d-flex">
<div className="col-1"></div>
<Design /* This is the line that fails */
data = {this.props.data}
/>
</div>
</div>
)}
}
class Main extends React.Component {
render(){
return(
<DesignedPage
page = {this.props.openPage} /*this could be any of page1-4 depending on button a click*/
data = {this.props.data}
/>
)}
}
Ideally this would render the react elements Design1 or Design2 based on what props.page is passed, but instead it returns
"Warning: <Design1 /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements." and "The tag <Design1> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter."
I've thought of making a long if, elseif, elseif.. statement in DesignedPage (the actual code has many more than 2 designs), which I'm fairly confident would work, but looks very messy in comparison.
You can't render the component name by getting its name as a string. You need to map the string to the component iteself:
let designs = {
"page1":Design1,
"page2":Design2,
}
If you pass a string, react would think it's a HTML tag, hence it say'Design1' tag is unrecognised. Also, you could import the components and use them as values in the designs object in place of strings.
let designs = {
"page1":Design1,
"page2":Design2,
"page3":Design1,
"page4":Design2
}
make one function that return react component..
getComponent = ({data, pageName}) => {
if(pageName === "page1") return <Desig1 />;
if(pageName === "page2") return <Design2 />;
}
and call function from render of DesignedPage component
const {page, data} = this.props;
return(
<div className="row flex-fill d-flex">
<div className="col-1">
{getComponent(page, data)}
</div>
</div>
)

How to build react component can change view type

I'm working a reactjs project have require to build a component can change view type. Like: the component have "view as icon" and "view as list", and have a button to switching eachother. Which the best way to archive that. Thanks
Edit: Sorry for making confuse
This is exactly what i want. As you can see, i have a mediaComponent and i want it can display in 2 different way: as a list and as thumbnail. So how can i do that with react. I don't actually have code for that.
p/s: I have tried conditional rendering but because it's different in render it's require to re-render the component with forceUpdate(). That's don't really look nice. So i hove there are another solution for that.
Render as thumbnail
Render as list
Something like that perhaps:
class MyComponent extends React.Component {
state = {
isList: false
}
toggleList = () => this.setState({ isList: !this.state.isList})
render(){
const { isList } = this.state;
return (
<div>
{isList ? <ListComponent /> : <GridComponent />}
<button onClick={this.toggleList}>Toggle</button>
</div>
)
}
}
export default MyComponent;

Unknown props warning when passing props to children

I'm trying to create a reusable dropdown menu wrapper component using this pattern:
class DropdownMenu extends React.Component {
render() {
return (
<span>
{React.cloneElement(
this.props.children,
{
menuOpen: this.props.menuOpen,
toggleMenu: this.props.toggleMenu
}
)}
</span>
);
}
}
const HeaderUserDropdown = ({menuOpen, toggleMenu }) => (
<DropdownMenu>
<div className={menuOpen ? 'visible' : ''}>
<button onClick={toggleMenu} />
</div>
</DropdownMenu>
)
But I get an error along the lines of Warning: Unknown props menuOpen, toggleMenu on <div> tag. Remove these props from the element. I know that I can use data- to get this working correctly, but that seems sort of hacky. What's the correct way to pass these props down to the children?
React distinguishes between HTML elements which are written in lower case (e.g. <div>) and React components which start with a capital letter.
In your code, you're trying to clone an HTML div element and add the properties menuOpen and toggleMenu, but these attributes are not supported by <div>, hence the warning. You need to set custom attributes on an HTML element, you'll need to use the data- prefix convention.

is there a way to have templates in react

I have built an element which is kind of template. (e.g, thumbnail container with image at the top and something in the footer with dynamic content between them)
the dynamic content can be different types of DOM elements, based on the state.
I did it with adding logic in the render method which "injects" the dynamic part.
Does this make sense (having logic in the render method which returns different react components)?
Is there a better way for templating? (i'm not looking for projects that add this capability, wanted to know if there's a "react way" to do so.
Thanks!
edit: here's the code I was referring to (coffeescript):
internalContent: ->
switch #props.title
when "title1" then SomeReactFactory(props)
when "title2" then SomeOtherReactFactory(props)
render ->
...
DOM.div
className: 'panel'
#internalContent()
the internalContent() method is dynamically adding some React Component based on the prop
This is the React way.. And you should make use of it to target your specific domain.
For example a Button in React could be written like this:
const MyButton = ({ text, type = "normal", color = "blue", onClick }) => {
return (
<button
onClick={onClick}
style={{backgroundColor: color }}
className={"my-button my-button--type" + type}>
{text}
</button>);
};
Or a layout component:
const MyLayout = ({side, nav, main}) => {
return (
<div className="container">
<nav>{nave}</nav>
<aside>{side}</aside>
<div className="main">{main}</div>
</div>
)
}
Now you can composite it for example like this:
class App extends Component {
...
render() {
<MyLayout
nav={<MyNav/>}
side={<MySideBar items={...} />}
main={<MyButton onClick={this.onClick} text="Main Button"}
/>
}
}
Dont try to pack everything in a big Component which will do everything, the trick in React is to make small reusable Components and composite them.
You could also create a bunch of components which you can use across many projects.

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