I am trying to build a component which,
Takes children and
Renders the children in the DOM and also,
Shows the children DOM in a pre for documentation sake
One solution is to pass the JSX as a separate prop as well. This makes it repetitive since I am already able to access it through this.props.children. Ideally, I just need to somehow convert the children prop as a string so that I can render it in a pre to show that "this code produces this result".
This is what I have so far
class DocumentationSection extends React.Component{
render(){
return <div className="section">
<h1 className="section__title">{heading || ""}</h1>
<div className="section__body"> {this.props.children}</div>
<pre className="section__src">
//Change this to produce a JSX string from the elements
{this.props.children}
</pre>
</div>;
}
}
How can I get the a jsx string in the format '<Div className='myDiv'>...</Div> when I render DocumentationSection as
<DocumentationSection heading='Heading 1'>
<Div className='myDiv'>...</Div>
</DocumentationSection>
Thanks.
Edit:
I tried toString, it dint work, gave [object Object]
If you want the HTML representation of a React element you can use #renderToString or #renderToStaticMarkup.
ReactDomServer.renderToString(<div>p</div>);
ReactDomServer.renderToStaticMarkup(<div>p</div>);
will produce
<div data-reactid=".1" data-react-checksum="-1666119235">p</div>
and
<div>p</div>
respectively. You will however not be able to render the parent as a string from within itself. If you need the parent too then from where you render the parent pass a renderToString version of it as props to itself.
React.renderToString is depreciated as of React v0.14.0, it's recommended to use ReactDOMServer.renderToString instead.
e.g
import ReactDOMServer from 'react-dom/server'
...
ReactDOMServer.renderToString(YourJSXElement())
You can use react-element-to-jsx-string:
import React from 'react';
import reactElementToJSXString from 'react-element-to-jsx-string';
console.log(reactElementToJSXString(<div a="1" b="2">Hello, world!</div>));
// <div
// a="1"
// b="2"
// >
// Hello, world!
// </div>
You can also use the pretty-format package, which is part of Jest, and used by Jest to show the diffs in JSX assertions.
import React from "react";
import prettyFormat from "pretty-format";
import renderer from 'react-test-renderer';
const { ReactTestComponent } = prettyFormat.plugins;
function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
console.log(
prettyFormat(renderer.create(<App />), {
plugins: [ReactTestComponent],
printFunctionName: true,
})
);
This will output something like:
<div
className="App"
>
<h1>
Hello CodeSandbox
</h1>
<h2>
Start editing to see some magic happen!
</h2>
</div>
You can try it by yourself in this CodeSandbox: https://codesandbox.io/s/xvxlw370xp
Related
so I wanted to do implement a sort of thing where I can put custom attributes in HTML elements which will be taken as props by a react component. Something like:
function someFunction(props) {
return <h1>props.something</h1>
}
HTML:
<div id="someElement" data-something="some text"></div>
renders:
<h1>some text</h1>
I THINK something like this could work, but I don't think its the best approach
let render_div = document.getElementById("someElement")
render(<someElement something={render_div.getAttribute("data-something")}/>, render_div)
I'm new to react so please help me :)
PS: I'm using typescript
Every data- attribute you set on an HTML element is available inside the element.dataset property. Keep in mind though that there's a naming conversion from dash-style to camelcase (see MDN).
What you should be able to do is something like the following:
<div id="root" data-title="title" data-my-text="my-injected-text" />
import ReactDOM from 'react-dom';
import App from './App';
const rootNode = document.getElementById('root');
const props = element.dataset; // { title: 'title', myText: 'my-injected-text' }
ReactDOM.render(App, props, rootNode);
So in React, I import a certain file using a statement like so:
import Search from './Pages/search';
However, what if the file I want to import is going to be different every single time I call up a certain function? Would something like this -
import {this.state.fileName} from './Pages/{this.state.extension}';
where the state values are filled in by an outside function, work like I want it to? Say I wanted to download the file that is associated with that dynamic import. Would something like this -
<a href = {this.state.fileName} download> download </a>
allow me to download the file? Is there a better way to go about this? Curious to know.
If the page you want to import is going to change frequently, then may be your component can accept a fileName prop and then you can use that prop to dynamically import that file.
Something like this may be: https://codesandbox.io/s/nervous-bush-xf1zg. Here, the filename changes with each DynamicPage call and the filename that is passed in is imported dynamically within the DynamicPage function.
**Didn't completely understand what you were trying to do but hope this helps
import React, { lazy, Suspense } from "react";
export default function App() {
return (
<div>
<DynamicPage fileName="File1" />
<DynamicPage fileName="File2" />
</div>
);
}
function DynamicPage({ fileName }) {
const DynamicFile = lazy(() => import(`./${fileName}`));
return (
<Suspense fallback={"Loading.."}>
<DynamicFile />
</Suspense>
);
}
import React from "react";
export default function File1() {
return (
<div>
<h1>File 1</h1>
</div>
);
}
import React from "react";
export default function File2() {
return (
<div>
<h1>File 2</h1>
</div>
);
}
It seems like you need to share state between components.
You can:
Pass it as props manually from the parent to each child
or use React ContextApi library
or use some library such as
Recoil
Redux (more complex)
I'm having an issue with this specific project where I cannot use a style class defined in "src/App.css" for a component in "src/components/layout/Header.js".
This is strange because I have another project that I use for notes and references where this works properly..
I'm being given the error:
The style prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + 'em'}} when using JSX.
src/App.css snippet:
.header-dock{
width:100%;
z-index:3
}
src/components/layout/Header.js snippet:
import React, { Component } from 'react';
export class Header extends Component {
render() {
return (<div className='header-dock'></div>);
}
}
Header tag is then added to the App.js return statement, and is in-turn rendered to the DOM on index.js.
replace
return <div className='header-dock'></div>;
into
return <div class='header-dock'></div>;
If you haven't, you need to import the CSS into your component file.
import './App.css'
This is assuming the component and CSS files are in the same folder.
EDIT:
The issue was a stray style prop in the component. After adding brackets style={{margin: '10px'}}, the issue was resolved.
I'm trying to follow along with the reactjs tic tac toe tutorial, but when I try to render the pages and I have this code
import React from 'react'
import ReactDOM from 'react-dom'
import Game from '../components/Game'
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(
console.log("render is working"),
<Game />,
document.getElementById('root'),
);
})
and the Game class looks like
import React from 'react'
import ReactDOM from 'react-dom'
import Board from './Board'
class Game extends React.Component {
render() {
console.log("Hooray, the game is working")
return (
<div className="game">
<div className="game-board">
<Board />
</div>
<div className="game-info">
<div>{/* status */}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
);
}
}
export default Game
The 'render is working prints to the console but the console log that says "Hooray, the game is working" doesn't show up, so it seems like the
Game class isn't getting rendered for some reason. I'm using ruby on rails with the built in webpacker to load all the javascripts
Cause calling console.log evaluates to undefined you are basically doing:
ReactDOM.render(
undefined, // <- react component
<Game />, // <- target
document.getElementById('root'), // < - useless
);
I'm surprised that ReactDOM.render is not throwing an error although you are passing completely useless arguments to it.
ReactDOM.render() takes 2 arguments: component, and mounting DOM element. You are passing a console.log for the first argument.
For one, why do you have your ReactDom.render(...) wrapped in that event listener?
Two, why are you passing the ReactDom.render(...) a console.log()? It should only take two arguments: a component, and the element it should be appended to. Try removing the console.log() and if that still doesn't work, try removing the event listener that wraps your ReactDoom.render(...).
You need to remove the console log from the ReactDOM render call.
If I need to render in a component, I can do it as such.
<div>
.....
{console.log('Inline console in render method')}
</div>
Your render should be as follows:
ReactDOM.render(<Game />, document.getElementById('root);
Assuming your child component is passed as 'export default Game' and your Id of your HTML base file is 'root'.
Render only takes two arguments. The first being the component being passed in and the second is the target in which it should place itself. You should only need to do this step at your 'index.js' file. Nowhere else.
I am trying to declare two different React elements that I would like to render. The both elements are separated elements such as displayed elements (App.jsx) and the customized account system (Login.jsx). But in my test I have the same code in the both jsx file to ensure that the issue is not related to a specific part of them.
I have also created an /imports/startup/client/index.js file (called in the /client/main.js file):
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import './accounts-config.js';
import App from '/imports/ui/App.jsx';
import Login from '/imports/ui/Login.jsx';
Meteor.startup(() => {
render(<App />, document.getElementById('app'));
render(<Login />, document.getElementById('login'));
})
and the /client/main.html contains the related div tags:
...
<div id="app"></div>
<div id="login"></div>
...
The issue is that the second render is never displayed (here, the div login) and I observe that only the first render is interpreted.
All the examples that I've found only deals with a single react element. So I wonder how to use several separated react elements like it is in my html file ?
I am newbie in the meteorjs and react world , so maybe I didn't get the right philosophy...
You can make use of React 16 new feature that is portal.
For how to use ReactDOM.createPortal please refer to following link:
How to use ReactDOM.createPortal() in React 16?
I have solved my issue using only one render in the Meteor.startup(() (in my index.js).
The React doc specifies that only one render can be declared in the Meteor.startup(() (in my index.js).
https://reactjs.org/docs/components-and-props.html
My code is the following:
in my index.js
Meteor.startup(() => {
render(<App />, document.getElementById('app'));
})
The trick is that this Super component (App.jsx) has to be used to call all the other components. In my example by calling the Login component:
render() {
return (
<div className="container">
<Login />
</div>
)
}
I believe because the render() statement has an implicit return statement as well, so since it can only execute and return one, the next render statement isn't executed.