Using React / JSX for Non-JavaScript Code Generation - javascript

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

Related

How can I fix 'warning Expected 'this' to be used by class method ' eslint error?

I am creating a PDF like this inside a react Component.
export class Test extends React.PureComponent {
savePDF() {
const source = document.getElementById('printContainer');
/* eslint new-cap: ["error", { "newIsCap": false }]*/
let pdf = new jspdf('p', 'pt', 'letter');
let margins = { top: 50,
left: 60,
width: 612
};
pdf.fromHTML(
source,
margins.left,
margins.top,
{
width: margins.width
},
() => {
pdf.save('worksheet.pdf');
}
);
}
and I am getting warning Expected 'this' to be used by class method 'savePDF' class-me
this is being called an click like this onClick={this.savePDF} see below
render() {
<Link
name="save-to-pdf"
onClick={this.savePDF}
button="secondary">
Save to PDF</Link>
<div id="printContainer" className="cf-app-segment--alt cf-hearings-worksheet">...
There are two different answers to this question, depending on how you want to handle it.
First, the reason you get this error is because of the ESLint rule https://eslint.org/docs/rules/class-methods-use-this. Specifically, this is because if something is a class method, e.g. if you are calling this.foo() to call a function, the whole reason to make it a method is because there are properties on this that you need to use.
While in many languages with class, most functions are methods, that is not the case in JS. If you have a class like
class Example {
constructor(){
this.data = 42;
}
someMethod() {
this.someHelper(this.data);
}
someHelper(value){
console.log(value);
}
}
the someHelper function would trigger the same error you are getting, because it never uses this, so you can just as easily do
class Example {
constructor(){
this.data = 42;
}
someMethod() {
someHelper(this.data);
}
}
function someHelper(value){
console.log(value);
}
In your case, you can do this. Your whole savePDF function could be moved outside of the class object.
That said, it is important to ask yourself why something like this isn't using this. In most cases, you'd expect any function that works with HTML to absolutely use this, because how else, in React, is it supposed to access the element's that React has created.
So the real answer to your question would be to drop the
const source = document.getElementById('printContainer');
line. If you need access to the HTML element being created by React, you should be using React's APIs to do so. That would be done with something like
class SavePDFButton extends React.Component {
constructor(props) {
super(props);
this.printContainer = null;
this.savePDF = this.savePDF.bind(this);
this.handlePrintContainerRef = this.handlePrintContainerRef.bind(this);
}
render() {
return (
<div>
<Link
name="save-to-pdf"
onClick={this.savePDF}
button="secondary"
>
Save to PDF
</Link>
<div
id="printContainer"
className="cf-app-segment--alt cf-hearings-worksheet"
ref={this.handlePrintContainerRef}
/>
</div>
);
}
handlePrintContainerRef(el) {
// When React renders the div, the "ref={this.handlePrintContainerRef}" will
// make it call this function, which will store a reference.
this.printContainer = el;
}
savePDF() {
// OLD: const source = document.getElementById('printContainer');
const source = this.printContainer;
// ...
}
}
I believe that's caused by the class-methods-use-this ESLint rule.
It's just letting you know that your function doesn't use this, so you can probably make it a static function.
turn it into static function
static savePDF() { ... }
Its happening because this function isnt using this meaning it dosnt need to be dynamic

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.

React Regex string to html

I am currently working on a project and running into a little issue with parsing messages for links. I was wondering if there a better way to handle the conversion of a string of html to html then using dangerouslySetInnerHTML. As the method says it could be dangerous and I rather look for a cleaner way to handle it.
//Function for regex
var www_reg = /(?:^|[^"'])(^|[^\/])(www\.[\S]+(\b|$))/gim;
message = message.replace(www_reg, '$1$2');
return {__html: message};
//Render
<div className='message-class' onClick={this.clickedLink} dangerouslySetInnerHTML={this.replaceURLWithHTMLLinks(display_message.last_message)}>
Something simple would be to split the string by the regex and turn the urls into JSX elements. Elegant solution that doesn't require dangerouslySetInnerHTML.
class Example extends React.Component {
constructor() {
super();
this.state = {
string: 'Hello www.example.com World www.stackoverflow.com',
message: [],
};
}
componentDidMount() {
let www_reg = /(www\.[\S]+(\b|$))/gim;
let blocks = this.state.string.split(www_reg);
let message = blocks.map(block => {
if (block.match(www_reg)) {
return <a href={'//' + block}>{block}</a>;
} else {
return block;
}
});
this.setState({ message });
}
render() {
return(
<div>{this.state.message}</div>
);
}
}
ReactDOM.render(<Example />, document.getElementById('View'));
<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>
<div id='View'></div>
PS: I had some trouble using your regex (it cut off some characters), so I modified it a bit. But it should work anyways, since I'm only using native JS functions here.
You could parse the message string into an array of text and URLs (keeping order) and then map over that turning the URLs into actual React link elements. You could then plop that array straight into your div <div>{arrayOfParts}</div>
It looks like there is an npm module react-linkify that will do that for you, or you can look at the source code here if you want to re-implement yourself.

Render JavaScript inside a <code> tag with JSX

I am using React to render some UI components. I would like to display some JavaScript code inside of <code> tag. I've tried the following:
class A extends React.Component {
render() {
return(
<pre><code>
(function(a, b) {
var s = a.prop;
// ...
}(c, d));
</code></pre>
);
}
}
When I try to compile this script with webpack and Babel, I get an unexpected token error at the line var s = a.prop. How can I properly render this JavaScript?
#Pamblam is right, you quite literally need a string:
render() {
var foo = `
(function(a, b) {
var s = a.prop;
// ...
}(c, d));
`
return (
<pre>
<code>{foo}</code>
</pre>
)
}
fiddle

Dynamically choosing tag in React

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.

Categories