How do I convert a string to jsx? - reactjs

How would I take a string, and convert it to jsx? For example, if I bring in a string from a textarea, how could I convert it to a React element;
var jsxString = document.getElementById('textarea').value;
What is the process I would use to convert this on the client? Is it possible?

You can consider using the React attribute dangerouslySetInnerHTML:
class YourComponent{
render() {
someHtml = '<div><strong>blablabla<strong><p>another blbla</p/></div>'
return (
<div className="Container" dangerouslySetInnerHTML={{__html: someHtml}}></div>
)
}
}

Personally, I love to do it just like in the previous answer which recommends the usage of dangerouslySetInnerHTML property in JSX.
Just for an alternative, nowadays there is a library called react-html-parser. You can check it and install from NPM registry at this URL: https://www.npmjs.com/package/react-html-parser. Today's weekly download statistic for that package is 23,696. Looks a quite popular library to use. Even it looks more convenient to use, my self, still need more read and further consideration before really using it.
Code snippet copied from the NPM page:
import React from 'react';
import ReactHtmlParser, { processNodes, convertNodeToElement, htmlparser2 } from 'react-html-parser';
class HtmlComponent extends React.Component {
render() {
const html = '<div>Example HTML string</div>';
return <div>{ ReactHtmlParser(html) }</div>;
}
}

Here's how you can do it, without using dangerouslySetInnerHTML.
import React from "react";
let getNodes = str =>
new DOMParser().parseFromString(str, "text/html").body.childNodes;
let createJSX = nodeArray => {
return nodeArray.map(node => {
let attributeObj = {};
const {
attributes,
localName,
childNodes,
nodeValue
} = node;
if (attributes) {
Array.from(attributes).forEach(attribute => {
if (attribute.name === "style") {
let styleAttributes = attribute.nodeValue.split(";");
let styleObj = {};
styleAttributes.forEach(attribute => {
let [key, value] = attribute.split(":");
styleObj[key] = value;
});
attributeObj[attribute.name] = styleObj;
} else {
attributeObj[attribute.name] = attribute.nodeValue;
}
});
}
return localName ?
React.createElement(
localName,
attributeObj,
childNodes && Array.isArray(Array.from(childNodes)) ?
createJSX(Array.from(childNodes)) :
[]
) :
nodeValue;
});
};
export const StringToJSX = props => {
return createJSX(Array.from(getNodes(props.domString)));
};
Import StringToJSX and pass the string in as props in the following format.
<StringToJSX domString={domString}/>
PS: I might have missed out on a few edge cases like attributes.

I came across this answer recently and, it was a good deal for me. You don't need to provide a string. Returning an array of JSX elements will do the trick.
We can store JSX elements in JavaScript array.
let arrUsers = [<li>Steve</li>,<li>Bob</li>,<li>Michael</li>];
and in your HTML (JSX) bind it like,
<ul>{arrUsers}</ul>
As simple as it is.

If you consider string
<div>Hello World</div>
If we are very strict, this actually is the valid JSX. The question is how to compile this JSX string into React code.
Easiest and the recommended way is to download some library like Babel and use it to transform the code. Babel can run in the Browser like the repl does.
It is also possible to transform JSX to other formats, but in this case you have to find a compiler or create one yourself.
The steps to create the JSX => React transformation yourself is:
transform the code string into AST representation
parse the AST and output code back to string
So you need somekind of AST parser like espree supporting JSX and then you can create a code which walks the AST tree and outputs something, like React -code out of it.
The AST tree of JSX data consists of normal JavaScript AST together with JSX nodes. The parser should walk through the tree and transform the JSX nodes into normal JavaScript code.
If you compile to React and encounter a JSX node with tag "div" you should compile that into React.createElement("div",... call with attributes and subnodes found under that AST node inserted as parameters of that call.
I have created a small AST Walker, which can process AST tree, ASTWalker, which can be used to transform the AST tree into some output format, like React or DOM.
On-line example of how to use it is here:
http://codepen.io/teroktolonen/pen/KzWVqx?editors=1010
The main code looks like this:
// here is the JSX string to parse
var codeStr = "<div>Hello world</div>";
var walker = ASTWalker({
defaultNamespace: "react",
});
// compile AST representation out of it.
var rawAST = espree.parse(codeStr, {
ecmaVersion: 6,
sourceType: "script",
// specify additional language features
ecmaFeatures: {
// enable JSX parsing
jsx: true
}
});
// then you can walk the walk to create the code
walker.startWalk( rawAST, {} );
var code = walker.getCode();
console.log(code);
document.getElementById("sourceCode").innerHTML = code;
DISCLAIMER: The library is not intented for compiling into React. It is mostly used with defaultNamespace: "DOM", using it to compile into plain JavaScript + DOM representation. Trying anything more complicated than simple tags may result as an error.
The important thing is to notice that React is not only possible output format for JSX.

I've been using html-to-react with some success (self closing tags cause a problem though, but a fix is in the pull requests...) to parse markup strings as DOM like objects, and in turn React elements. It's not pretty, and if you can avoid it, do so. But it gets the job done.
html-to-react at github: https://github.com/mikenikles/html-to-react

Use React-JSX-Parser
You can use the React-JSX-Parser library dedicated for this.
npm install react-jsx-parser
here is the repo

html-react-parser is what you need.
import parse from 'html-react-parser';
import React from 'react';
export default function YourComponent() {
someHtml = '<div><strong>blablabla<strong><p>another blbla</p/></div>'
return (
<div className="Container">{parse(someHtml)}</div>
)
}

Here's a little utility component for this:
const RawHtml = ({ children="", tag: Tag = 'div', ...props }) =>
<Tag { ...props } dangerouslySetInnerHTML={{ __html: children }}/>;
Sample usage:
<RawHtml tag={'span'} style={{'font-weight':'bold'}}>
{"Lorem<br/>ipsum"}
</RawHtml>

First you can change it to a non-string array and then use it as JSX
class ObjReplicate extends React.Component {
constructor(props) {
super(props);
this.state = { myText: '' };
}
textChange=(e) =>{this.setState({ myText: e.target.value })};
render() {
const toShow = this.state.myText;
var i=1;
var allObjs=new Array;
while (i<100){
i++;
allObjs[i] = <p>{toShow}</p>; //non-sting array to use in JSX
}
return (
<div>
<input onChange={this.textChange}></input>
{allObjs}
</div>
);
}
}
ReactDOM.render(<ObjReplicate/>,document.getElementById('root'));

You need to use babel with preset react
npm install --save-dev babel-cli babel-preset-react
Add the following line to your .babelrc file:
{
"presets": ["react"]
}
Then run
./node_modules/.bin/babel script.js --out-file script-compiled.js
You can find more info here

Related

I want to write other languages code in react js

I am working on building a website.
I made all things, but now I'm stuck in adding code to the website.
I want to put some codes inside the JSX component but it is having some problems with adding { <<these types of symbols.
Is there any way I can write the C++ code or C code inside the react element?
import React from 'react'
const Template = () => {
return (
<div>
<h1></h1>
</div>
)
}
export default Template
The JSX won't appreciate the "{ <<", but if you want a quick in on this, you may try something like this -
const SomeCode = ()=><code>{`#include <stdio.h>;`}</code>
That might not be sufficient - you may need proper highlighting with specific programming language, formatting, etc. for what you might be building. But just for getting literals such as << working with JSX - you may take the above example as base.
In JSX, which is what react usees, brackets will be parsed.
Therefore, strings should be inside {`content`}, or you can define that code as a string, and place it inside jsx as below
const SomeComponent = ()=>{
const codeSnippet = `{ << whatever code blahblah`
return <div>
{codeSnippet}
</div>
}

Converting HTMLElement to a React Element, and keeping any event listeners

Rendering some JSON data into a set of collapsible HTML elements is EASY using a library like renderjson (npm package) for Vanilla JS
const data = { sample: [1, 2, 3, 4], data: { a: 1, b: 2, c: ["hello", null] } };
const rjson = renderjson(data);
document.getElementById('to-render').append(rjson);
In The react world
However, renderjson returns an HTMLElement, NOT A STRING. Please note that I'm not JUST trying to convert HTML strings into react HTML here. I'm trying to convert the HTMLElement into a real ReactElement.
So, I actually managed to do this, but since I'm parsing the HTMLElement into a string, all the onClick event listeners generated by renderjson on the + and - handles (to open or collapse the json) are LOST in the process....
Does anyone have any idea on how to convert a HTMLElement object into React Element, keeping all the original event handlers?
I guess, the real question here is: how can the renderjson package be "repackaged" to become React friendly?
Here is a codesandbox to make it easier to see what I'm talking about here.
Good question, I keep running into similar issues with non-React JS libraries that return DOM elements. These libraries should be ported to React, but if you don't want to do that, you can use findDomNode() to get a DOM (not React) node, and append your HTMLElement there. Please note that in the official docs, its use is discouraged because "it pierces the component abstraction". Anyways, in your example, you could add a wrapper class:
class JsonTreeWrapper extends Component {
componentDidMount() {
const renderedJson = renderjson(this.props.data);
const container = findDOMNode(this);
container.appendChild(renderedJson);
}
render() {
return <div/>;
}
}
what this does is render a div, and when the component mounts it finds the div and adds the HTMLElement generated by your library there. Simple.
In your App, you can use it like this:
class App extends Component {
render() {
return (
<div>
<h1> Inside the react world </h1>
<JsonTreeWrapper data={data}/>
</div>
);
}
}
This way your listeners will be working just fine.
Codesandbox didn't work for me, but you can check a demo here. I hope this helps!

Reuse react's PropTypes in tests and other parts of code?

Is there an easy/straightforward way to re-use the validation built in to React.PropTypes progrmatically in tests and other code?
For example, if I have:
ButtonX.propTypes = {
className: React.PropTypes.string
};
If I load that button with a numerical classname, react will warn in the console.
I would like to re-use all that logic in code, and do something like:
validateButtonUsingReactPropTypes({ className: 'my-class' })
Is there a known clean way of getting acces to those internals?
Check out the Test Utilities article in the docs... It think it would provide some examples of how you can access the validators for testing.
Example using renderIntoDocument, based on code from ReactJSXElementValidator-test.js in the React GitHub repository.
// arrange
RequiredPropComponent = class extends React.Component {
render() {
return <span>{this.props.prop}</span>;
}
};
RequiredPropComponent.displayName = 'RequiredPropComponent';
RequiredPropComponent.propTypes = {prop: React.PropTypes.string.isRequired};
// act
ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop={null} />);
// assert
let testPassed = console.error.calls.count() == 0
You can obviously incorporate this into some good testing tools, but this is just the raw call.

How to add functions to ReactElement

I'm trying to use an internal Javascript library which is normally used for the web and which contains millions of lines of code (hence why I would really like to use it rather than change anything in it :D) with React Native.
This library has some DOM manipulations in it which are abstracted, and I'm trying to make the abstraction work using React Native.
Basically, what we do on the web is that we pass an HTML element to the library, and the library fills it with other elements. Later on, this library may remove elements from the tree or add some, depending on what we're telling it to do.
So I'd like to be able to pass this library a ReactElement, so that React Native handles the drawing part automatically.
Here is what the library does, very very simplified:
function libraryCode(element) {
element.append(otherElement);
}
var element = document.createElementNS('div');
libraryCode(element);
return element;
And here is what I'd like to be able to do:
render() {
var element = React.createElement(Element);
libraryCode(element);
return element;
}
But ReactElement doesn't have an append function. So I was wondering if there was a way to add functions to ReactElement someway. It would allow me to create a function called append that would add the child and re-render the element. I've tried and it's readonly obviously. I wanted to extend the ReactElement class but how to tell React to use my class instead of ReactElement?
var elem = React.createElement(Element);
elem.append = function() {};
I'm also open to new ideas, I'm quite new to React/React Native so maybe I'm going all wrong with this! :)
Thanks
What you need to do is use React.cloneElement and then do your appending by passing in children. This should allow you to do:
function libraryCode(element, otherElement) {
return React.cloneElement(element, null, [otherElement]);
}
EDIT
The closest I got to getting it to work but not conform to your needs is this:
var oldCreateElement = React.createElement;
React.createElement = function(...args) {
let element = Object.assign({}, oldCreateElement(...args));
element.append = (otherElement) => {
// Unfortunately you simply can't replace the
// reference of `element` by the result of this operation
return React.cloneElement(element, null, [
React.cloneElement(otherElement, {
// You must give a `key` or React will give you a warning
key: Math.random(),
}),
]);
};
return element;
};
This will only let you do:
var element = React.createElement(Element);
element = element.append(<Text>Hi</Text>);
return element;
But not:
var element = React.createElement(Element);
element.append(<Text>Hi</Text>);
return element;
Anyway, this is very hacky and NOT recommended and frowned upon because it requires monkey-patching.

Output object name other than React with jsx syntax

with React v0.12 the #jsx pragma is gone which means it is no longer possible to output jsx with anything other than the React.METHODNAME syntax.
For my use case I am trying to wrap the React object in another object to provide some convenience methods thus, in my component files, I want to be able to write:
var myConvenienceObject = require('React-Wrapper');
var Component = myConvenienceObject.createSpecializedClass({
render: function () {
return <div />
}
})
However the jsx compiler automatially converts <div /> into React.createElement("div", null)
With older versions of React it was possible to handle this using the pragma at the top of the file. However, since that has been removed, I was wondering if there was any way currently to change the name of the object compiled by jsx so <div /> would be transformed into myConvenienceObject.createElement("div", null)
No, it's no longer possible to use a custom prefix for JSX. If you need to do this, you'll need to modify the JSX transform code, or create a fake React.
var React = require('react'), FakeReact = Object.assign({}, React, {
createElement: function(component, props, ...children){
// ...
// eventually call the real one
return React.createElement(component, props, ...children);
}
});
module.exports = FakeReact;
And then to use it you import the fake react and call it React.
var React = require('fake-react');
// ...
render: function(){ return <div />; }
If you would like to make some elements contains in your myConvenienceObject, you could consider the children props as shown in the doc. But this may need some changes in the myConvenienceObject too, to accept the children.
By the way, i'm not sure where is this createSpecializedClass functions comes from and what it does

Resources