Reactjs How to insert react component into string and then render - reactjs

How to create a reactjs component that will render the props data with another component.
for example I have a sentence say "Hello guys this is {{name}}. How are you.". Now I want to replace the name with the reactjs component.
when I try to replace the name with the component it shows as [object object].
First Edit:
var sentence = "Hello guys this is {{name}}. How are you.";
var tag_values = {'name': 'any Name'}
TagBox will take sentence and tag_value as props and replace the tags with the Tag component. and render it
var TagBox = React.createClass({
render: function(){
// replacing the tags with Tag component
this.props.sentence = this.props.sentence.replace(tags_values['name'], <Tag \>)
return(
<div>
{this.props.sentence} //Issue: This will Print as "Hello guys this is [Object Object]. How are you."
// But this should print as "This will Print as Hello guys this is any Name. How are you."
// After clicking on "any Name" it should be replaced with input.
</div>
);
}
})
Tag Component will replace the tag with input box on double click. and again replace input box with data on enter.
This can be done using state.
var Tag = React.createClass({})

Okay, so assuming that's a string you have as input, you need to create an array.
var parts = str.split(/\{\{|\}\}/g);
// => ["Hello guys this is ", "name", ". How are you."]
The odd items are literal strings, and the even parts are the stuff between the brackets.
Now we'll create a helper function called mapAlternate. Which takes a function to call for odd elements, and a function to call for even elements in our array.
function mapAlternate(array, fn1, fn2, thisArg) {
var fn = fn1, output = [];
for (var i=0; i<array.length; i++){
output[i] = fn.call(thisArg, array[i], i, array);
// toggle between the two functions
fn = fn === fn1 ? fn2 : fn1;
}
return output;
}
Now we can do something like this in our component:
render: function(){
var parts = str.split(/\{\{|\}\}/g);
// render the values in <strong> tags
var children = mapAlternate(parts,
function(x){ return <span>{x}</span>; },
function(x){ return <strong>{x}</strong> });
return <div>{children}</div>;
}
Which gives us: "Hello guys this is name. How are you."

Have you heard of React String Replace ?
Here is a stateless component example:
import replace from 'react-string-replace';
const reg = /\{([a-z|A-Z|0-9|\.]+)\}/g;
const OutputComponent = props => {
var str = 'Hello {name}, this is a "Super" component: {Super}';
var output = replace(str, reg, prop => props.replacements[prop]);
return <div>{output}</div>;
}
// later
import Super from './Super.jsx';
const obj = {
Super: <Super />,
name: 'John'
}
return <OutputComponent replacements={obj} />;

I just fixed this issue with react-jsx-parser
Your Example would be:
import JsxParser from 'react-jsx-parser'
export default class TagBox extends React.Component {
render() {
const sentence = "Hello guys this is <Tag>name</Tag>. How are you." // simply include the component in your string
return(
<JsxParser components={{ Tag }} jsx={ sentence } /> // identify the component in your string to inject
)
}
}

Nothing from above doesn't worked for me unfortunately. Here is a useful stable solution regexify-string (npm)
npm install --save regexify-string
Works like a charm
regexifyString({
pattern: /\[.*?\]/gim,
decorator: (match, index) => {
return (
<Link
to={SOME_ROUTE}
onClick={onClick}
>
{match}
</Link>
);
},
input: 'Some initial string with [link]',
});

Related

call function in React functional component

I am trying to call a function from a div like the following
<div id='div_abstract'>
{content.abstract && content.abstract.length ? (
<article id="abstract" onMouseUp={spanSelect}>{content.abstract </article>) : ''}
</div>
My functional component is structured like this
export default function ExternalInfos(props) {
...
function spanSelect() { ... }
return(
...
);
}
And the function I'm trying to call is
let table = [];
function spanSelect() {
var span = document.createElement("span");
span.setAttribute("id","span");
if (window.getSelection()) {
var text = window.getSelection();
if (text.rangeCount) {
var range = text.getRangeAt(0).cloneRange();
range.surroundContents(span);
text.removeAllRanges();
text.addRange(range);
};
};
let object = window.getSelection().toString();
table.push(object);
const annotation = document.getElementById("annotationArea");
annotation.updateObjectAnnotation(table);
}
But nothing happens when I select text from my div and it doesn't return an error.
How do I solve this?
You need to capitalize the event handler prop: onMouseUp.
From the React docs (https://reactjs.org/docs/handling-events.html):
"React events are named using camelCase, rather than lowercase."

How to resolve "serializes to the same string" message with Jest?

In my React app, I've built a function that accepts a string full of regular text and any number of URLs. It then converts these into a <span> in React with every URL inside of an <a href tag. The code works really well but I can't seem to write a Jest test for it.
Here's what I've tried so far:
expect(convertHyperlinks('http://stackoverflow.com'))
.toStrictEqual(<span><a href='http://stackoverflow.com' target='_blank'>stackoverflow.com</a></span>);
And:
expect(convertHyperlinks('http://stackoverflow.com'))
.toMatchInlineSnapshot(<span><a href='http://stackoverflow.com' target='_blank'>stackoverflow.com</a></span>);
In the former case I'm getting the "serializes to the same string" message.
In the latter case, it's showing me this:
Expected properties: <span>stackoverflow.com</span>
Received value: <span>stackoverflow.com</span>
Might anyone know how to build a passing test for this?
Robert
Update: Here's the code for the function in question:
export const convertHyperlinks = (text: string): React.Node => {
// Find all http instances
const regex = /http\S*/g;
const hyperlinkInstances = text.match(regex);
if (!hyperlinkInstances) {
return <span>{text}</span>;
}
// Break up `text` into its logical chunks of strings and hyperlinks
let items = [];
let idx1 = 0;
let idx2 = -1;
hyperlinkInstances.forEach((hyperlink) => {
idx2 = text.indexOf(hyperlink, idx1);
if (idx2 === idx1) {
items.push(hyperlink);
idx1 += hyperlink.length;
} else {
items.push(text.substring(idx1, idx2));
items.push(hyperlink);
idx1 = idx2 + hyperlink.length;
}
});
if (idx1 < text.length) {
items.push(text.substring(idx1, text.length));
}
return (
<span>
{items.map((item) => {
if (item.includes('http://')) {
const plainLink = item.replace('http://', '');
return (
<a href={item.toLowerCase()} target='_blank' key={plainLink}>
{plainLink}
</a>
);
} else {
return item;
}
})}
</span>
);
};
You are returning a ReactNode from the method, which is an object. But you are trying to assert as just a string. It would'nt work.
This is what you may be getting back from the method,
And so, you must assert against the object you got, and not the way you are doing it right now,
const result = convertHyperlinks('http://stackoverflow.com')
expect(result.props[0].key).equals('stackoverflow.com');
// similar kind of assertions.
Additionally, I would suggest you go the component route and just render the component in the test method and assert for presence of elements as opposed to diving into react objects.
A representation of the same is as follows,
Here is your component,
const ConvertToHyperlinks = ({text}: {text: string}) => {
// your logic and then returning DOM elements.
return <></>;
}
Then you use it anywhere as,
<div>
<ConvertToHyperlinks text={'https://www.test.com/'} />
</div>
In your unit test you can then,
const renderedComponent = render(<ConvertToHyperlinks text={''https://www.anytyhing.com}/>);
expect(renderdComponent.getByText('anytyhing.com')).ToBeInTheDocument();
Here I am using some Rect Testing Library method but the idea is same even if you use enzyme etc.

Programmatically generated JSX string

I have a text file that I'd like to convert to a JSX string using Regex and display on the browser as if it's a JSX markup manually entered in the render return () block.
Let's say I create a string
generatedString = '<span class="tag" style={divStyle}>Test string</span>'
and want to have it displayed in the browser by
render() {
<div> {generatedString} <div>
}
The browser shows
"<span_class="tag"_style={divStyle}>Test string</span>"
as a text string, instead of "Test string" with the style applied to it.
You can use the dangerouslySetInnerHTML property on the div tag to render HTML as you like. This would still however be just displayed as HTML and not as a jsx tag, like this:
render() {
return <div dangerouslySetInnerHTML={generatedString} />;
}
If you anyhow generate the JSX string yourself, then why not choose create a class that renders those properties, like in the following fiddle
var GeneratedComponent = React.createClass({
propTypes: {
content: React.PropTypes.object
},
render() {
var content = this.props.content;
if (!content || !content.tag) {
return null;
}
return React.createElement(content.tag, content);
}
});

i need React databinding (Arr push after data refresh)

I am faced with the problem
web page is to react with the signal.
Signal does not regularly.
my Scenarios (Arr data push after refresh)
It does not give any one event
Because I can not use. setState funciton
i think javascript function call for react databind refresh
Because the data binding, you use the following dataRefresh() functions.
I know code is incorrect.
I've written code like the following:
var dataArr = [{
key : '1',
text : "hello1",
title: "title1"
},
{
key : '2',
text : "hello2",
title: "title2"
},
{
key : '3',
text : "hello3",
title: "title3"
}
];
var Repeat = React.createClass({
render : function(){
var data = this.props.items;
return(
<PanelGroup accordion >
{ data.map(function(item){
return(
<Panel header={item.title} eventKey={item.key} >
{item.text}
</Panel>
);
})}
</PanelGroup>
);
}
});
function startReact(){
React.render(
<div>
<Repeat items={ dataArr }/>
</div>,
document.getElementById('content')
);
}
startReact();
function dataRefresh(){
dataArr.push({
key : '4',
text : "hello4",
title: "title4"
});
startReact();
}
setTimeout("dataChange()",3000);
It is the question.
I need to have an idea that can solve the problem.
Advice is required.
That's a bad idea. When you have new data use setState so it will update/rerender your view automatically that's the point of react.
Bind your update to a function that update the state not directly to the render.
Here is an example very easy it explain how to update your state when the user is clicking on some button:
https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html
So for your instead of handling a user action you'll set an handler that is called when you have new data.
I hope it's clear
To go in React way, you must use state instead of that above method.
Use getInitialState to point to the global dataArr and then use setState to update the state.
Even I would suggest putting dataArr in the base component holding the child components. This will avoid polluting the global namespace as well.
Inside your setTimeout, avoid using string. instead wrap it inside a function like below:
setTimeout(function() {
dataChange();
}, 3000);
So the code will become:
var Repeater = React.createClass({
getInitialState: function() {
return {
data: dataArr
}
},
componentDidMount: function() {
setTimeout(function() {
// update the dataArr
// Instead of calling dataChange gloabl method, I would put it inside the base component and call this.updateData();
// this.setState({data: dataArr});
}.bind(this),3000);
},
updateData: function() {
// increment the array and call
this.setState({data: dataArr});
},
render : function() {
return (
<div>
<Repeat items={ this.state.data}/>
</div>
);
}
});
The below code will become:
function startReact(){
React.render(<Repeater />,
document.getElementById('content')
);
}

Render an Element with React

Honestly I don't think that this is the best Title, but I've no idea how explain it.
So sorry for it.
I'm trying to write a Component that parse all links(thar are not into a anchor tag) and emoji and render it like links or image.
For emoji I'm using this amazing component: https://github.com/banyan/react-emoji
It works well, but the problem is with the simple links...I don't have found a way for render it like links, instead of text of the link tag.
This is my code:
# #cjsx React.DOM
#Linkify = React.createClass
displayName: 'Linkify'
mixins: [ReactEmoji]
componentDidMount: ->
componentWillUnmount: ->
render: ->
<div>
{#parseLinks(#props.text)}
</div>
parseLinks: (text) ->
pattern = /(ht|f)tps?:\/\/[^\"<]*?(?=\s|$|<\/[^a]>)/gi
results = text.match(pattern)
new_text = text
if results and results.length > 0
for result in results
link_html = React.createElement('a', {href: result}, result)
new_text = new_text.replace(result, link_html)
return #emojify(new_text)
and if I wrote:
Hello search here google.com :)
I get:
Hello search here [object Object] :) (instead of :) I've the correct emoji image)
The problem is: why it don't show correctly the Object Link ? where I done wrong ?
Thanks for any help.
link_html = React.createElement('a', {href: result}, result)
new_text = new_text.replace(result, link_html)
You can't use String#replace to put an object (returned by React.createElement) into a string. It's like saying
var text = "one two three";
var el = {testing: true};
alert(text.replace("two", el));
Instead, you should return a ReactElement (created with JSX or React.createElement) that contains the associated text, but with the link in the correct place in the children.
Consider the output of
<span>Test google.com link</span>
which is
React.createElement("span", null,
"Test ",
React.createElement("a", {href: "google.com"},
"google.com"
),
" link"
)

Resources