Dynamically choosing tag in React - javascript

I have some React components that use SVG, and can be contained within an SVG or not. Consequently, I'd like to have a simple way of letting them render inside svg or g, depending on a param, something like:
export default class SVGComponent extends React.Component{
get containerElement() {
return this.props.inSVG ? 'g' : 'svg';
}
render() {
return(<{containerElement}>
<text>Extendable component</text>
</{containerElement}>);
}
}
This doesn't work: you can't use template injection in JSX this way. Is there another way I could approach it? It seems like I might be able to do React.DOM[containerElement], but I gather React does not like mixing React.DOM and JSX, and I don't want to move entirely over to React.DOM syntax just for this one function.

Take a look at this answer Dynamic tag name in jsx and React
It seems you can create a variable with the first letter capitalized and use it like a component like this
const CustomTag = `h${this.props.priority}`;
<CustomTag>Hello</CustomTag>
If this.props.priority == 3 this will render <h3>Hello</h3>

You can do it like this
render(){
var content = <text>Extendable component</text>;
if(this.props.inSVG){
return (
<g>{content}</g>
);
}
else {
return (
<svg>{content}</svgg>
);
}
}

I would probably do something like this:
export default class SVGComponent extends React.Component{
get containerElement() {
var content = (<text>Extendable component</text>);
return this.props.inSVG ? (<g>{content}</g>) : (<svg>{content}</svg>);
}
render() {
return({containerElement()});
}
}
But, it really depends on how dynamic the <text> is.
If the child elements are very dynamic, you might want to render this.props.children and add then as children of the component.

Related

Performance implications of defining render props function within render()?

Take the following example code:
class Something extends Component {
render() {
return (
<SomeProvider
render={providedProps => (
<SomeChild {...providedProps}/>
)}
/>
)
}
}
Every React render props article uses this pattern as an example, but it's generally bad practice to define functions inline. Is this an exception to that rule?
Is there any benefit to defining the function outside of render?
class Something extends Component {
renderSomeChild = providedProps => (
<SomeChild {...providedProps}/>
)
render() {
return (
<SomeProvider
render={this.renderSomeChild}
/>
)
}
}
At this "function outside case" the const renderSomeChild (i suposed it is cause you did'nt mention) just make a reference to an allocated function in memory. So, if you aren't planning to reuse that function it make no sense to use it outside, i really think this make no significant on performance...
A way to use the first example as the best practices is to write a code like this:
class Something extends Component {
render() {
return (
<SomeProvider>
{providedProps => (
<SomeChild {...providedProps}/>
)}
</SomeProvider>
)
}
}
I hope the above code will help you! :)
My suspicion was correct that the renderProps function should not be re-declared within the render function - or else it will force re-renders. I confirmed this Why Did You Render?

Using React / JSX for Non-JavaScript Code Generation

I was wondering if you could use React/JSX in a code generation framework.
Is there a way to do something like the code below in JSX?
var className = "Person"
return (
//// public class {className}
//// {
////
//// }
);
Where the //// would be some special character or character sequence that signals to JSX parser that this should just be plain text?
Or is there a better approach using React that already exists?
If you're doing this in the browser you can do something like this and then grab the textContent of the <code> element that was rendered when it mounts. I haven't used React on the server but I'm guessing you could use ReactDOMSever's renderToString and then create a simple script that strips the opening and closing tags from your string and you have your code in a text string that you could save to anytype of file using node.
var data = {
class_name : 'User',
method1Name: 'doSomething'
}
class MakeClass extends React.Component {
componentDidMount(){
console.log(this.codeElem.textContent)
}
render() {
let {json} = this.props;
return (
<code ref={c=>this.codeElem=c}>{`
class ${json.class_name} {
constructor() {
// do something
}
${json.method1Name}() {
// this method does something
}
}
`}</code>
)
}
}
ReactDOM.render(
React.createElement(MakeClass, {json: data}),
document.getElementById('root')
);
You can see the full thing here:
http://jsbin.com/rizaqifasi/edit?html,js,console,output

How to access DOM event-handlers for JSX-components in server-sided React

I am building a simple static view-engine using React with the goal of rendering static HTML-markup and generating a js-file filled with that components DOM-events (onClick, etc).
The way I'm doing the first part is to require a specified JSX-file which, for example, looks like this:
import React from 'React';
export default class Test extends React.Component {
clicked() {
alert('Clicked the header!');
}
render() {
return (
<html>
<head>
<title>{this.props.title}</title>
</head>
<body>
<h1 onClick={this.clicked}>click me!!!</h1>
</body>
</html>
);
}
}
I am then rendering the JSX-file via a NodeJS-backend like this:
let view = require('path-to-the-jsx-file');
view = view.default || view;
const ViewElement = React.createFactory(view);
let output = ReactDOMServer.renderToStaticMarkup(ViewElement(props));
It works great for serving static HTML. But I am wondering if there is a way to access all components used in the JSX-file in an array or something, which I then could use to check what events are bound and to which handlers.
So in this example, be able to get that the <h1>-tag's onClick-handler? Is this even possible to do somehow?
To be able to get the function as a string from the onClick event, we want the following:
The DOM of the element
We can obtain this by attaching a ref attribute on our h1 element
The name of the function being passed into the onClick event (clicked)
The function itself from a string containing the name of the function
Since we're conveniently using methods within a React component, we can use this['functionName'] within our component to obtain the function.
A stringified version of the function
import React from 'React';
export default class Test extends React.Component {
componentDidMount() {
// Gets the name of the function passed inside onClick()
const nameBound = this.element.props.onClick.name;
// Removes 'bound ' from the function name (-> clicked)
const nameString = nameBound.replace('bound ', '');
// Gets the function from the function name as a string
const convertedFunction = this[nameString];
// Converts the function into string
const stringifiedFunction = convertedFunction.toString();
console.log(functionString);
}
clicked() {
alert('Clicked the header!');
}
render() {
return (
<html>
<head>
<title>{this.props.title}</title>
</head>
<body>
<h1 ref={(element) => { this.element = element; }} onClick={this.clicked}>click me!!!</h1>
</body>
</html>
);
}
}
After a lot of messing around I came up with a solution that works quite well.
If I create my own instance of the ReactElement I want to render (in the example ViewElement(props)), I can then render the element using it's standard render-function:
let element = ViewElement(props);
let instance = new element.type();
let render = instance.render();
From here I can go through all the props for this element, so, say, onClick-handlers would be in render.props.
So what I do is to check each prop if the key matches a react-event-name (ex. onClick, onDragEnd, onDragEnter etc). If it does, and the value of this property is of type function - I have the event-name and it's handler-function:
Object.keys(render.props).map((key) => {
if (bigArrayOfAllTheEventNames.indexOf(key) !== -1) {
item.events[key] = render.props[key];//check if type is function.
}
});
Then I also iterate through the render.props.children recursivly to reach all it's child components and add every component which has events to an array.
The only problem left was that I needed a way to bind the rendered DOM-string to the javascript handlers I now have. For this I added a need to use a custom DOM-attribute, which then can be used to ID the component with something like this
$("[data-id=value]").on({event-name}, {it's JS-handler}).
It might not be perfect yet, but I think that this is the best solution out there.

How to dynamically request a React Component based on a variable value?

I'm wondering how to dynamically request a component based on a variable value. What I'm trying to accomplish here is the following:
import Template1 from './Template1.jsx';
import Template2 from './Template2.jsx';
var ComponentTemplate = (some_condition === true) ? "Template1" : "Template2"
render() {
<ComponentTemplate prop1="prop1 val" />
}
Is this even possible? If so, how?
It is not clear to me why you need to use a string representation of a class rather than just switch the component or use a conditional render:
var Component = some_condition === true ? Template1 : Template2;
// ...
return ( <Component /> );
But assuming this is an oversimplification, the easiest thing to do would be to use a mapping object that translates a string into a component. With ES2015 enhanced object literals, it becomes fairly straightforward:
var Components = {
Template1,
Template2,
};
var Component = condition ? Components['Template1'] : Components['Template2'];
// ...
return ( <Component /> );
If you are just looking to render different component based on the condition you could actually have 2 other render function and inside render() you could check the condition and call corresponding render
render () {
!this.state.isAuthorized? renderLogin(): renderTweets();
}
renderLogin () {
<LoginView/>
}
renderTweet () {
<ListTweets/>
}
Hope this is what you were looking for!
All the dynamic rendering should inside Render function. Because the JSX compile will depend on the Variable name not the Object reference.
If you write <Component />,
it will transfer to React.createElement('Component').
Cannot refer to the dynamic component you want to choose.
depend on the condition when Render is running. Use different React tag you want.
render() {
condition ? <Template1 /> : <Template2 />
}

React.js without JSX - "Warning: Something is calling a React component directly. Use a factory or JSX instead"

I'm trying to use React.js component without JSX and receive such warning:
Warning: Something is calling a React component directly. Use a factory or JSX instead. See: http://fb.me/react-legacyfactory
I've visited link but suggested createFactory solution didn't help me :/
app.js
var React = require('react/addons');
var TagsInput = React.createFactory(require('./tagsinput')); // no luck
var TagsComponent = React.createClass({
displayName: "TagsComponent",
saveTags: function () {
console.log('tags: ', this.refs.tags.getTags().join(', '));
},
render: function () {
return (
React.createElement("div", null,
React.createElement(TagsInput, {ref: "tags", tags: ["tag1", "tag2"]}),
React.createElement("button", {onClick: this.saveTags}, "Save")
)
);
}
});
React.render(React.createElement(TagsComponent, null), document.getElementById('tags'));
tagsinput.js
https://raw.githubusercontent.com/olahol/react-tagsinput/master/react-tagsinput.js
I cannot figure out what is the problem here?
React.createClass(spec) returns a component.
React.createElement(component, props, ...children) creates an element.
React.createFactory(component) returns a factory function, which can be used to create an element.
React.createFactory(a)(b, c, d) is the same as React.createElement(a, b, c, d).
You get the warning when you call a component directly, like component(). If you want to call it like a function, use createFactory
var factory = React.createFactory(component);
var element = factory(props, ...children);
Or use createElement:
var element = React.createElement(component, props, ...children);
In 0.13 this will give an error instead of a warning:
var element = component(props, ...children);
Also because React.DOM is going away, you should create dom elements the same way you create component based elements
Edit: looks like React.DOM is sticking around for now.
var div = React.createFactory('div');
var element = div(props, ...children);
// or
var element = React.createElement('div', props, ...children);
Bold used to show consistent terms. ...children means any number of child arguments
You need to wrap all of your child components in createFactory as well, I was able to get your code to run without that specific warning by wrapping Tag and Input in createFactory.
jsbin
For me. The culprit is recompose. I want to convert my functional component to pure. Then I solved it by using memo.
Using memo from react:
import React, { memo } from 'react';
const Component = (props) {
return (
// Component code
)
}
// Wrap component using "memo" HOC
export default memo(Component);
Using pure from recompose:
import React from 'react';
import { pure } from 'recompose';
const Component = (props) {
return (
// Component code
)
}
// Wrap component using "pure" HOC
export default pure(Component);

Categories