I'm writing a documentation website based on React. I want to show the code that is necessary to use a given component from my framework. At the same time I would like to show the actual component running, like a side-by-side view.
Currently, I'm adding the component as a String for the reference implementation and the component as JSX for the running scenario. Something like this:
var ButtonDoc = React.createClass({
render: function () {
let buttonComponent = (
<Button label="Add" />
);
let buttonCode = `<Button label="Add" />`;
return (
<div>
{buttonComponent}
<pre><code>{buttonCode}</code></pre>
</div>
);
}
});
Question: Is there a way that I can get the string representation of the given React component without the need to replicate the code?
I'm expecting something like this:
var ButtonDoc = React.createClass({
render: function () {
let buttonComponent = (
<Button label="Add" />
);
let buttonCode = `${buttonComponent}`;
return (
<div>
{buttonComponent}
<pre><code>{buttonCode}</code></pre>
</div>
);
}
});
The output of the given code is object [object].
As I did not find anything that solved my problem, I ended up creating a npm repository to achieve this task.
https://github.com/alansouzati/jsx-to-string
Usage:
import React from 'react';
import jsxToString from 'jsx-to-string';
let Basic = React.createClass({
render() {
return (
<div />
);
}
}); //this is your react component
console.log(jsxToString(<Basic test1="test" />)); //outputs: <Basic test1="test" />
This is super late but in case anyone read this half a decade later (React v17, Native 0.68), you can also just use curly braces inside of backticks: `${integerToString}`. This will convert your embedded value to string.
Related
I'm learning React and I'm trying to render a form with a few different form-steps (details, payments, confirm...) from a UI library that has Form component ready, which iterates through those form-steps.
The issue is that each 'step' component looks more like an actual JS object:
export const DetailStep = (
<div>Details screen with inputs, texts and images...</div>
);
It doesn't have the = () => so it's not an actual functional component, so I can't add hooks or functions into it.
That's the array of steps:
const stepPages = [
DetailStep,
PaymentStep,
ConfirmStep
];
And that's the actual Form component that iterates through the array of steps:
<Form
onSubmitClick={onStepSubmit}
render={formRenderProps => {stepPages[step]} }
/>
If I'll change any step to a functional component (DetailStep = () => ...), the step will be blank and won't be rendered.
How can I fix that / just add hooks to each 'step' component?
JSX declarations are converted into React.createElement function calls, this does mean that the stepPages in your example are objects (just that they are react objects)
The magic that turns them into function components happens in the render prop of the form:
<Form
onSubmitClick={onStepSubmit}
render={formRenderProps => {stepPages[step]} }
/>
You can see that it's passing a function in here, that returns the jsx objects. This is kinda the same as putting the jsx directly in the render function.
Although I notice that in your example, the jsx isn't being returned, so I would expect this example to not work correctly.
The longform equivalent would be something like this:
<Form
onSubmitClick={onStepSubmit}
render={formRenderProps => {
if (step === 0) {
return (
<div>Details screen with inputs, texts and images...</div>
)
} else if (step === 1) {
return (
<div>Details screen with inputs, texts and images...</div>
)
} // etc
}}
/>
EDIT to answer question in comments
You're right, so how can I add hooks into those object components?
If each of the form steps needs to use hooks and manage it's internal state, then I would make them functional components instead of JSX objects, then pick the right component from the stepPages map and render it like so:
export const DetailStep = (props) => {
// hooks and stuff here
return (
<div>Details screen with inputs, texts and images...</div>
);
}
const StepComponent = stepPages[step];
return (
<Form
onSubmitClick={onStepSubmit}
render={formRenderProps => <StepComponent {...formRenderProps} />}
/>
);
import React from "react";
class App extends React.Component {
render() {
var formula = "<div> hello </div>"
const div = document.createElement("div")
div.innerHTML = formula
return (
<div>
{div}
</div>
);
}
}
export default App;
I get an error
Error: Objects are not valid as a React child (found: [object HTMLLIElement]). If you meant to render a collection of children, use an array instead.
Basically the string has html elements I want to set inner html to the div constant. Right now yeah its relatively simple I just need to know if this can be done
My original code has something like this littered throughout my code in recursions too
var formula = "<div>"
formula = formula + "something"
formula = formula + "</div>
You don't need to create a element you can directly use :
class App extends React.Component {
render() {
const formula =<div> hello </div>
return (
<div>
{formula}
</div>
);
}
}
export default App;
This cannot be done this way in React. A component can only return a ReactElement or JSXElement. If for some reason you absolutely need to set HTML from a string (there are reasons for this), you have to use dangerouslySetInnerHTML which was made for that precise purpose, but exposes potential security vulnerabilities as explained in the documentation.
You can accomplish this as detailed in the official example:
import React from "react";
class App extends React.Component {
createMarkup() {
return {__html: 'Hello'};
}
render() {
return (
<div dangerouslySetInnerHTML={createMarkup()}/>
);
}
}
export default App;
But again, this is discouraged and I'm curious to know why you can't just return JSX markup as the other answers suggest.
Why are you creating div like that? I’m begginer in ReactJs ,but I think you shouldn’t touch / create HTML DOM Elements like that at all or if it is not neccesary.
You can simply just do this .
Let formula = <div> hello </div>
Return(
<div>
{formula}
</div>
)
You can do it with useState.
import {useState} from "react";
const App = () => {
const [formula, setFormula] = useState("");
setFormula(<div> 'hello' </div>);
return(
<div>
{formula}
</div>
);
}
export default App;
This may be a complete noob question, but I am having trouble figuring this out. I have a React component, and inside its render method, I want to render some dynamically passed in components (through props).
Here's my return from the render method (ES6, transpiled with Babel)
return (
<div className={openState}>
/* Shortened for brevity*/
{this.props.facets.map(function(f) {
var CompName = facetMap[f.type]
return <CompName key={f.id} />
})}
/* Shortened for brevity*/
</div>
)
facetMap is simply a map to match up a string to the "type" passed in. I can't seem to get the components to actually render on the page.
I had the result of the .map() method stored in a var, and then logged that to the console, and it showed an array of components. However {facetComponents} in the render method had the same results.
EDIT, for those interested in the solution
Because I genuinely could not find an answer anywhere, here's my completed solution, thanks to #Mathletics
//Components imported using ES6 modules
import OCFacetText from "./oc-facet-text"
// Etc...
//Later on, in my render method:
let facetMap = {
"text": OCFacetText,
"number": OCFacetNumber,
"numberrange": OCFacetNumberRange,
"boolean": OCFacetBoolean,
"checkbox": OCFacetCheckbox,
// Etc, etc...
}
let facetComps = facets.map(function(f) {
var CompName = facetMap[f.type]
return <CompName key={f.id} />
})
//Finally, to render the components:
return (
<div className={openState}>
/* Shortened for brevity*/
{facetComps}
/* Shortened for brevity*/
</div>
)
From what I can gather, it sounds like your facetMap is a collection of strings:
var facetMap = {text: 'OCFacetText'}
but it should actually return component classes:
var OCFacetText = React.createClass({...});
var facetMap = {text: OCFacetText};
Then (and only then) you will need to use the createElement method to construct your component dynamically:
var CompName = facetMap[f.type]; // must be a FUNCTION not a string
return React.createElement(CompName, {key: f.id}) />
Annoying one.
If you want dynamic component names you'll have to utilize React.createElement(CompName), where CompName is a string or the class object.
By default react will look for a component named CompName in your above example.
So your render function would look like so:
return (
<div className={openState}>
/* Shortened for brevity*/
{this.props.facets.map(function(f) {
var CompName = facetMap[f.type]
return React.createElement(CompName, {key: f.id}) />
})}
/* Shortened for brevity*/
</div>
)
I've got this very basic component:
Tile = React.createClass({
render: function(){
var styles = [
TileStyles.tile
];
return (
<div style={styles} test="test" />
);
}
});
Unfortunately it is producing this html:
<div style="0:[object Object];" data-reactid=".0.$0"></div>
Why does it give me [object object] instead of the inline styles?
Obviously I don't need to use an array here but I do on a more complex component.
What am I doing wrong?
UPDATE:
Thanks for the answers guys but the issue is I WANT to be able to use multiple styles.
aka use TileStyles.tile and TileStyles.active under certain circumstances.
The problem is (as already stated) that you give the style property an array, but an object is expected.
So simply changing the code to this:
Tile = React.createClass({
render: function(){
var style = _.extend({},TileStyles.tile,TileStyles.active);
return (
<div style={style} test="test" />
);
}
});
Here _ is a dependency on either lodash or underscore. This will work if you defined TileStyles as something like:
var TileStyles = {
tile: { width: '200px', height: '200px', backgroundColor: 'blue' },
active: { backgroundColor: 'green' }
}
If you don't want a dependency on _, it is possible to do it by hand, but it can be a hassle.
Update 2016-03-29:
Instead of relying on a dependency like lodash or underscore or doing it by hand, you can use the new Object.assign feature in ecmascript 2015.
var style = Object.assign({},TileStyles.tile,TileStyles.active);
Or take the example fully updated to ecmascript 2015:
class Tile extends React.Component {
render() {
const style = Object.assign({},TileStyles.tile,TileStyles.active);
return <div style={style} test="test" />;
}
}
React is expecting an object to be passed as styles argument, not an array, so you will need to change it to
Tile = React.createClass({
render: function(){
return (
<div style={TileStyles} test="test" />
);
}
});
You will have to make sure that TileStyles returns an (or is an) object and is accesible within the scope, otherwise, create a function in the class scope and call it with
this.TileStyles()
I am using react, and I am trying to pass props/context to my dynamic childrens,
by dymamic childrens I mean childrens are render using
{this.props.children}
How can I pass to this children (In my code I know it's type) context/props?
In this jsbin there is an example that it dosen't work on dynamic childrens.
http://jsbin.com/puhilabike/1/edit?html,js,output
Though #WiredPrairie's answer is correct, the React.addons.cloneWithProps is deprecated as of React v0.13RC. The updated way to do this is to use React.cloneElement. An example:
renderedChildren = React.Children.map(this.props.children, function (child) {
return React.cloneElement(child, { parentValue: self.props.parentValue });
});
There's not a a great way to do this that is clear and passing all the properties of the parent isn't a great pattern and could lead to some very difficult to follow code if not done carefully (and with excellent documentation). If you have a subset of properties though, it's straightforward:
JsFiddle
Assuming you're using React with Addons, you can clone the children of a React component and set new property values on them. Here, the code just copies a property called parentValue into each child. It needs to create a clone of each element as the child element had already been created.
var Hello = React.createClass({
render: function() {
var self = this;
var renderedChildren = React.Children.map(this.props.children,
function(child) {
// create a copy that includes addtional property values
// as needed
return React.addons.cloneWithProps(child,
{ parentValue: self.props.parentValue } );
});
return (<div>
{ renderedChildren }
</div>)
;
}
});
var SimpleChild = React.createClass({
render: function() {
return <div>Simple { this.props.id }, from parent={ this.props.parentValue }</div>
}
});
React.render((<Hello parentValue="fromParent">
<SimpleChild id="1" />
<SimpleChild id="2" />
</Hello>), document.body);
Produces:
Simple 1, from parent=fromParent
Simple 2, from parent=fromParent
Spreading props on DOM elements
https://github.com/vasanthk/react-bits/blob/master/anti-patterns/07.spreading-props-dom.md
When we spread props we run into the risk of adding unknown HTML
attributes, which is a bad practice.
const Sample = () => (<Spread flag={true} domProps={{className: "content"}}/>);
const Spread = (props) => (<div {...props.domProps}>Test</div>);