How to output text in ReactJS without wrapping it in span - javascript

In my ReactJS-based application I do:
var _ = React.DOM;
_.span(null, 'some text', _.select(null, ...));
The problem is: 'some text' is wrapped in additional span element in the DOM. Is there any way to avoid this behavior and just output raw text?
To be clear: I want to output
<span>some text<select>...</select></span>
not
<span><span>some text</span><select>...</select></span>

Update: This is now "fixed" in React v15 (2016-04-06) – now comment nodes are added around each piece of text, but it is no longer wrapped in a <span> tag.
We received some amazing contributions from the community in this release, and we would like to highlight this pull request by Michael Wiencek in particular. Thanks to Michael’s work, React 15 no longer emits extra <span> nodes around the text, making the DOM output much cleaner. This was a longstanding annoyance for React users so it’s exciting to accept this as an outside contribution.
Full release notes.
This is currently a technical limitation of React; it wraps any floating text nodes in a span so that it can assign it an ID and refer back to it later. In a future version of React hopefully we can remove this restriction.

You can hard code the html as a last resort.
<option value={value} dangerouslySetInnerHTML={{__html: value}}></option>

Well.. If you're hell bent on doing this, and accept the limitation that you cannot access props or state, you could do this:
var Component = React.createClass({
displayName:"Statics",
statics: {
customRender: function(foo) {
return React.renderToStaticMarkup(<div
dangerouslySetInnerHTML={{__html: foo.bar }}/>);
}
},
render: function () {
return <div dangerouslySetInnerHTML={{__html:
<Component.customRender bar="<h1>This is rendered
with renderToStaticMarkup</h1>" />}} />
}
});
renderToStaticMarkup will not insert any spans or react-dataid, and is meant for static server rendering. It's probably not a great idea to do this, but there you go.
renderToStaticMarkup
Similar to renderToString, except this doesn't create extra DOM
attributes such as data-react-id, that React uses internally. This is
useful if you want to use React as a simple static page generator, as
stripping away the extra attributes can save lots of bytes.
Check the result at: http://learnreact.robbestad.com/#/static

I Changed the version of react and react-dom and worked perfect
"react": "^15.0.1",
"react-dom": "^15.0.1"

Related

Best way to include custom react components between strings/p-tags?

tldr: New to frontend. Trying to include custom components within p-tags for a website, I've tried various methods but can't seem to get it to work unless I hard code the content into the return bit in my react component - this isn't viable as I would like to have many p-tags which would change on my website when a user presses next.
Hi everyone! I'm new to front end programming, and this is my very first question, so please excuse any incorrect terminology and/or question formatting!
I'm currently working on a react project where I have created custom components to include in my webpage. These components work when placed between p-tags.
For example, I made a custom component and it works as expected when I do something like:
function test{
return(
<p>Hello! This is a <ShowDefinition word="website"/> which I made using react! </p>
)}
However, I intend to have lots of content which would change using an incremental index, so I've placed my content in a separate jsx file to store as a dictionary.
I found that when doing something like this:
function test{
return(
<div>{script[index].content}</div>
)};
where
script[index].content = '<p>Hello! This is a <ShowDefinition word="website"/> which I made using react! </p>';
it just shows up as a string literal on the webpage. I've tried to wrap my string in {} but this did not seem to work.
I've also tried dangerouslySetInnerHTML with a dompurification to sanitise the html code
dangerouslySetInnerHTML={{__html: DOMPurify.sanitize(script[index].content)}};
This worked however it excluded all of my custom components. So for the example sentence it would show up on the page as "Hello! This is a which I made using react!"
I understand now this doesn't work because dangerouslySetInnerHTML cannot convert custom components/only accepts html, however I am now at a complete lost as to what to do.
I have thought of storing the content in a md file then parsing it however I have little knowledge of md files/md parsers and from what I've found I don't think solves my problem?
Any advice would be greatly appreciated.
Thank you so much.
Ok, so first of all, this is definitely not how you should think when playing with React. Even if this is technically possible with things like React.createElement or dangerouslySetInnerHTML, I suggest you look at this first. I will help you get the thinking in react.
However if I had to do this in React, I would probably use a custom hooks or any conditional logic to render my jsx.
codesandbox
import "./styles.css";
import React from "react";
const useContentFromIndex = (index) => {
return () => {
if (index === 0) return <p> Index 0 </p>;
if (index === 1) return <p> Index 1 </p>;
return <p> Index 2 </p>;
};
};
export default function App({ index = 0 }) {
const CustomContent = useContentFromIndex(index);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<CustomContent />
</div>
);
}

Set a default component for Html Elements in React

I've been playing with reactjs a bit lately. As I understand, ReactJS will convert html elements into something like this:
<div><span>Text</span></div>
Into
React.createElement('div', null, React.createElement('span', null, 'Text'))
So far so good, but what I'd like to do is have special component that will override all html elements so some kind of basic behavior can be added to any html element using props.
So instead of the above, I'd want something like this instead:
React.createElement(Html('div'), null, React.createElement(Html('span'), null, 'Text'))
This would make it possible to make usual html a bit more dynamic without having to define a new component each time. One alternative in my mind to this would be to have Component based on the props. So adding a particular prop would give a particular trait to the component above.
One example would be to have something like this:
<div foreach="collection" as="value">
<div my-value="value" />
</div>
Just to explain a bit further, the idea is mainly to adapt React to different templating engines. So it's not necessary to rewrite completely templates to work in React and gives some kind of compatibility layer between a legacy templating engine.
So rewriting as this isn't what I'm asking. I already know how to write directly for React.
<div>
{
this.collection.map((value) => {
return <div>{value}</div>
})
}
</div>
One alternative for me which would be quite easy if React can't do that easily is to transform the html elements as Uppercase or differently so React thinks it's a component instead of html.

Adding a html element in a string in react

I know this is not the React way of doing things but it's something i need and i just wanna try a solution.
After an action in my page, i display a JSON with
JSON.stringify(myJSON, null, 4);
This change triggers the render in my class.
Inside this json i have a timestamp value, that i translate into readable time. I do this before stringifying, like this:
myJson.timestamp = `${myJson.timestamp} ( ${newDate(parseInt(myJson.timestamp) * 1000)} )`
Now comes the weird stuff:
I need to have a button right next to this translated timestamp. In the previous version of my app i was doing this easily like this:
myJson.timestamp = myJson.timestamp + "( " + newDate(parseInt(myJson.timestamp) * 1000) + " ) <button>action!</button>"
which rendered my button in the page.
But react just writes it as a string, without rendering it. I guess this happens because ReactDOM doesn't parse that as a new HTML element because render is not triggered anywhere.
I would like to know if it is possible to do something like this in react and how. As you can see it's pretty complicated to try to render the button in that place the react way and i have no idea how to actually do it.
#AnaLizaPandac gave me the proper solution to this problem. dangerouslySetInnerHTML was what i am looking for, but i want to take a moment to go a bit deeper into why this solution is usually wrong and why i actually need it. Here you can find the docs for this property.
As you should know, innerHTML is bad because it exposes your web pages to XSS attacks. Why would i employ this solution though? This page does not contain any shared information with any other user and it doesn't contain any vulnerable inputs. I just needed to decorate my JSON object with a button next to the timestamp, that when clicked, redirects to another public page (like google.com). I believe this behavior doesn't expose my app to XSS and solves my problem in a simple elegant way.
I hope i am now wrong with regard to how dom-based XSS works, in case i misunderstood something, leave a comment.
Edeph,
I may be misdiagnosing the problem you are experiencing since I cannot see the entire React component that is misbehaving.
However, based on what you described, I believe the problem is that you are colocating both JSX (i.e., <button>action!</button>) and javascript code. This is certainly doable and a common usecase for React; however, it would need to be done some way similar to this:
myJson.timestamp = myJson.timestamp + "( " + newDate(parseInt(myJson.timestamp) * 1000) + " )
const MyButton () {
return (
<div>
{myJson.timestamp}
<button>action!</button>
</div>
);
}
The key here is JSX expressions should always be enclosed in parens, and JS expressions colocated with JSX need to be enclosed in curly brackets.
I hope this is helpful.
Rendering components from an array
If you have an array of timestamps, you can render the button component like so:
return myTimestamps.map(timestamp => {
const myButton () {
return (
<div>
{timestamp}
<button>action!</button>
</div>
);
}
}
This will result in a column of timestamps, each with a button.
I would actually like to ask a question in the comments to give you a full answer but I've still not enough reputation to do so.
You have to use the following attribute: dangerouslySetInnerHTML={{__html: {yourHtmlAsString}} like this:
render: function() {
return (
<div className="content" dangerouslySetInnerHTML={{__html: thisIsMyCopy}}></div>
);
}
Here's a working sample:
https://codesandbox.io/s/v8x56yv52l
Having said that, there are ways to circumvent this problem in most cases, unless you return dynamically rendered HTML from your server-side app that can differ in its structure a lot. But if the structure of the HTML is mostly the same and only the content changes, you can dynamically render or omit parts of your JSX like this:
return (
{props.conditionMet ? <div>props.anyContentYouLike</div> : null}
);
You can also build on the above idea and come up with much more complicated solutions:
render () {
innerHTML = props.conditionMet ? <div>props.anyContentYouLike</div> : null};
return innerHTML;
}
Hope that helps.

Render custom React component within HTML string from server

I have a HTML string that comes from the server, for example:
const myString = '<p>Here goes the text [[dropdown]] and it continues</p>`;
And I split this string into 3 parts so the result is the following:
const splitString = [
'<p>Here goes the text ',
'[[dropdown]]',
' and it continues</p>'
];
Then I process those 3 parts in order to replace the dropdown with a React component:
const processedArr = splitString.map((item) => {
if (/* condition that checks if it's `[[dropdown]]` */) {
return <Dropdown />;
}
return item;
}
So after all, I get the processed array, which looks like this:
['<p>Here goes the text ', <Dropdown />, ' and it continues</p>']
When I render that, it renders the HTML as a text (obviously) with the Dropdown component (that renders properly) in between the text. The problem here is that I cannot use { __html: ... } because it has to be used such as <div dangerouslySetInnerHTML={{ __html: ... }} />. I cannot add <div> around the string because that would cut out the <p> tag.
I thought about splitting the parts into tags and then in some sort of loop doing something like:
React.createElement(tagName, null, firstTextPart, reactComponent, secondTextPart);
but that would require fairly complex logic because there could be multiple [[dropdown]]s within one <p> tag and there could be nested tags as well.
So I'm stuck right now. Maybe I'm looking at the problem from a very strange angle and this could be accomplished differently in React. I know that React community discourages rendering HTML from strings, but I cannot go around this, I always have to receive the text from the server.
The only stackoverflow question I found relevant was this one, however that supposes that content coming from backend has always the same structure so it cannot be used in my case where content can be anything.
EDIT:
After some more digging, I found this question and answer which seems to be kinda solving my problem. But it still feels a bit odd to use react-dom/server package with its renderToString method to translate my component into a string and then concatenate it. But I'll give it a try and will post more info if it works and fits my needs.
So after playing with the code, I finally came to a "solution". It's not perfect, but I haven't found any other way to accomplish my task.
I don't process the splitString the way I did. The .map will look a bit different:
// Reset before `.map` and also set it up in your component's constructor.
this.dropdownComponents = [];
const processedArr = splitString.map((item) => {
if (/* condition that checks if it's `[[dropdown]]` */) {
const DROPDOWN_SELECTOR = `dropdown-${/* unique id here */}`;
this.dropdownComponents.push({
component: <Dropdown />,
selector: DROPDOWN_SELECTOR
});
return `<span id="${DROPDOWN_SELECTOR}"></span>`;
}
return item;
}).join('');
Then for componentDidMount and componentDidUpdate, call the following method:
_renderDropdowns() {
this.dropdownComponents.forEach((dropdownComponent) => {
const container = document.getElementById(dropdownComponent.selector);
ReactDOM.render(dropdownComponent.component, container);
});
}
It will make sure that what's within the span tag with a particular dropdown id will be replaced by the component. Having above method in componentDidMount and componentDidUpdate makes sure that when you pass any new props, the props will be updated. In my example I don't pass any props, but in real-world example you'd normally pass props.
So after all, I didn't have to use react-dom/server renderToString.
How about break the text apart and render the component separately?
your react component should look like this (in JSX):
<div>
<span>first text</span>
{props.children} // the react component you pass to render
<span>second part of the text</span>
</div>
and you would just call out this component with something like:
<MessageWrapper>
<DropdownComponent/> // or whatever
</MessageWrapper>

What is the use of components in ReactJS instead of a single variable

I started to study ReactJS and can't understand the purpose of components. for instance lets have a component:
var QuoteMaker = React.createClass({
render: function () {
return (
<blockquote>
<p>
The world is full of objects, more or less interesting; I do not wish to add any more.
</p>
<cite>
<a target="_blank"
href="#">
Douglas Huebler
</a>
</cite>
</blockquote>
);}});
why can't we just use
var thisVar = (
<blockquote>
<p>
The world is full of objects, more or less interesting; I do not wish to add any more.
</p>
<cite>
<a target="_blank"
href="#">
Douglas Huebler
</a>
</cite>
</blockquote>
)
instead of that component.
You could. That would be just fine.
What you couldn't do is have a list of those, built from dynamic data.
What if you wanted multiple quotes from multiple people?
What if you wanted a button that randomized which quote appeared?
What if you wanted a twitter feed, or a blog roll, or a comment section?
You don't necessarily need the createClass. That's a particularly old way of looking at things, and you should really be using a Babel build pipeline; full stop.
You can simply say:
const Quote = (props) => (
<blockquote>
<p>{ props.quote }</p>
<cite >{ props.author }</cite>
</blockquote>
);
ReactDOM.render(<Quote quote="..." author="..." />, rootEl);
Now you can happily build a randomizer, or a quote API, or a quote editor, or whatever. The component does what it should, and it leans on input from the outside world for what it shouldn't do.
React allow you to get a composant approach. So you can use as a new balise text (composant) in react. You can reuse this component, it will have the same behavior everywhere.
Functional components
That way that you have in example can be created function, or stateless components, where we can use props object, that can be passed from the parent object.
const Component = (props) => {
//access to props
}
Class component
Class components allows to store thier own state and use it to rerender the component, via this.setState() function:
class Component extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
//access to both state and props
}
Why we must use components instead of js objects? Because react has its own DOM - virtual DOM, where it listening to changes and rerendering your real DOM, you can reuse or extend this component anywhere and its also semanticly awesome.
Take a look to the documentation about React components.
There are lots of uses for components. These are few:
Allegedly, if your component has no logics or special use, just flat html, so can pass using component, although it is more encapsulated and looks more neat.
If now or in the future you'll want to use some logics for your component, so it can be handled in the component. That is the Object Oriented approach for using html block of code. If your'e coming from Angular, you can think of it as "directive", from .Net "User control". Well, not exactly, but the purpose is same.
Calls and uses to different libraries. It is a better practice not to require libraries as global variable, but only for your use in the component.
And of course the most important factor: You can take advantage of the react "digest loop". You can choose what to do on constructing, before rendering, after rendering, and much more.
You can look it up here: https://facebook.github.io/react/docs/react-component.html
As i said before, there are lots of uses for components over flat html, and basically this is the whole point of ReactJS as component based :)

Categories

Resources