I'm trying to replay any occurrence of a link in a text as a link, but I only get it to show the example.com as text in the message. (Just to be clear, it shows other message content if there is other content in the message than the link, but the link gets "linkified(as seen below)")
Here is the message component.
import React from "react";
import moment from "moment";
import { Comment, Image } from "semantic-ui-react";
const isOwnMessage = (message, user) => {
return message.user.id === user.uid ? "message__self" : "";
};
const isImage = message => {
return message.hasOwnProperty("image");
};
const timeFromNow = timestamp => moment(timestamp).fromNow();
function linkify(text) {
var urlRegex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/ig;
return text.replace(urlRegex, function(url) {
return '' + url + '';
});
}
const Message = ({ message, user }) => (
<Comment>
<Comment.Avatar src={message.user.avatar} />
<Comment.Content className={isOwnMessage(message, user)}>
<Comment.Author as="a">{message.user.name}</Comment.Author>
<Comment.Metadata>{timeFromNow(message.timestamp)}</Comment.Metadata>
{isImage(message) ? (
<Image src={message.image} className="message__image" />
) : (
<div>
<Comment.Text>{linkify(message.content)}</Comment.Text>
<React.Fragment dangerouslySetInnerHTML={linkify(message.content)}>
</React.Fragment>
</div>
)}
</Comment.Content>
</Comment>
);
export default Message;
dangerouslySetInnerHTML prop needs an object with __html property
don't use React.Fragment when using dangerouslySetInnerHTML. Just use a div
Working demo
Code Snippet
function linkify(text) {
var urlRegex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/gi;
return text.replace(urlRegex, function(url) {
return '' + url + "";
});
}
const text = `I'm trying to replay any occurrence of a link in a text as a link, but I only get it to show the example.com as text in the message. (Just to be clear, it shows other message content if there is other content in the message than the link, but the link gets "linkified(as seen below)")`;
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<br />
<div dangerouslySetInnerHTML={{ __html: linkify(text) }} />; }
</div>
);
}
you better treat your url variable first by your regex then return like a valid react element:
function linkify(text) {
const urlRegex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/ig;
// extract your url by urlRegex something like
const url = text.match(urlRegex)[0]
return <a href={url} >{url}</a>
}
Related
I have a dynamic page named [className].js. In it, I'm trying the fetch the data using getServerSideProps. Currently, I have only two sets of data. When I click a link, that's supposed to take me the dynamic page with the className and show the data within it. But, no matter which link I click, it always shows the data of the first one.
Why is this happening? How can I get the data of the specified className?
export default function classPage({ classDetail }) {
const capitalizedClassTitle =
classDetail.title.charAt(0).toUpperCase() + classDetail.title.slice(1);
const classTitle = capitalizedClassTitle.replace(/-/g, " ");
return (
<div>
<NavBar />
<div>This is the page for {classTitle}</div>
<div className="w-20 h-20">
<Image
src={classDetail.classImageURL}
width={classDetail.imageWidth}
height={classDetail.imageHeight}
/>
</div>
</div>
);
}
export async function getServerSideProps({ query: { className } }) {
const res = await fetch(
`http://localhost:3000/api/admin/classes/${className}`
);
const { data } = await res.json();
return { props: { classDetail: data } };
}
I found the solution. In the api pages of [className], I had this line of code wrong.
const classDetail = await Class.findOne({className});
I changed it to await Class.findOne({title: className}) and now it is working as I wished. title was the keyword for className in my Class schema.
I am trying to display the data I fetched from an API which is a nested array. The json file looks like this for one pool and the devices inside that pool:
[
{
"id": "staging",
"pool": "some name",
"status": "FAILED",
"deviceMonitoringEntries": [
{
"deviceDescriptor":{
"id": "Apple_TV_HD1",
}
]
}
]
I want to display the id of the pool first and then display the devices assigned to the pool by displaying the id in deviceDescriptor.
My code is like this:
import React, { useEffect, useState } from 'react'
import axios from 'axios'
function Pool(){
const url = 'http://localhost:8043/pools'
const [pool, setPool] = useState(null)
let content = null
useEffect(() => {
axios.get(url)
.then(response =>{
setPool(response.data)
})
}, [url])
if(pool){
console.log("in if pool")
console.log(pool)
return (
content=
<>
{pool.map((id) => {
<h3 key = {id}>{id}</h3>
return (
<>{pool.map(({ deviceMonitoringEntries}) => (
deviceMonitoringEntries.map((deviceDescriptor) => (
<p key = {deviceDescriptor.id}> {deviceDescriptor.id}</p>
))
))}</>
);
})}
</>
)
}
return(
<div>
{content}
</div>
)
}
export default Pool
However, the header <h3 key = {id}>{id}</h3> never prints. I can display the header and paragraph separately but it does not work together. I am very new to React and I would appreciate your help!
As per React documentation
React components implement a render() method that takes input data and returns what to display.
In the functional component, you directly add a return statement at the end.
You cannot assign variables like this in your code.
return (
content=
<>
...
</>
You got the response and you stored that value in a state variable. Use the variable and render your content in the final return statement.
{
pool.map((p) => (
<>
<h3 key={p.id}>{p.id}</h3>
{p.deviceMonitoringEntries.map((device) => (
<p key={device?.deviceDescriptor?.id}>{device.deviceDescriptor?.id}</p>
))}
</>
));
}
You can try like that in your code and I am attaching a sandbox for reference here.
My case:
I am combining Laravel (laravel/ui scaffolding) and React App.
This is my first time trying this, and found myself stuck in getting data from BE to FE.
I Looks like I am getting the data, but my array of $testData is converted into a string When being logged from the Dataset of that element. I am not sure what I should do to have my array back to a json format instead.
the code:
A Controller sending my data:
public function index()
{
$testData = [
["name" => "Lucy"],
["name" => "Kurt"],
["name" => "Emma"],
];
return view('intern.index')->with('testData', $testData);
}
I have my blade, loading a div with a certain id:
#extends('layouts.app')
#section('body')
<div id="react-app" data-list={{ json_encode($testData) }} ></div>
#endsection
And my react component app.js that is rendered on the blade view:
function App( props ) {
console.log(props.list)
return (
<div className="container">
Hello World!
</div>
);
}
export default App;
if (document.getElementById('react-app')) {
const thisElement = document.getElementById('react-app');
let props = Object.assign({}, thisElement.dataset);
console.log(props)
/* The restult I am getting from that log:
{
list: "{{\"name\":\"Lucy\"},{\"name\":\"Kurt\"},{\"name\":\"Emma\"}}"
}
*/
ReactDOM.render(<App list={props.list} />, thisElement);
}
Update:
The solution was to simply parse the result back.
if (document.getElementById('react-app')) {
const thisElement = document.getElementById('react-app');
let props = Object.assign({}, thisElement.dataset);
console.log(props)
/* The restult I am getting from that log:
{
list: "{{\"name\":\"Lucy\"},{\"name\":\"Kurt\"},{\"name\":\"Emma\"}}"
}
*/
ReactDOM.render(<App list={JSON.parse(props.list)} />, thisElement);
}
I'm using react-native-render-html to render html.
The renderers method allows me to provide custom function to render a particular tag. However I want to replace the children with my custom component, using the raw inner HTML from the source.
Consider. I have the below html snippet provided to <HTML /> component:
<a> <b> <c meta="xyz"> Some text </c> <b> </a>
and I have a custom renderer which returns a component that takes a html string and does some magic with it:
const renderers = {
c: () => (
<Custom html={/** how do I get "<c meta="xyz"> Some text </c>"? */} />
)
}
The API was not initially designed to handle these kind of use cases, but as for version 5.0.0, it is very easy!
Version 6.x
import * as React from 'react';
import HTML, {domNodeToHTMLString} from 'react-native-render-html';
function CustomRenderer({ tnode, style, key }) {
const html = React.useMemo(() => domNodeToHTMLString(tnode.domNode), [tnode]);
return <Custom key={key} html={html} style={style} />;
}
Version 5.x
Since version 5, it is extremely easy with the help of the new domNodeToHTMLString util, see snippet below:
import * as React from 'react';
import HTML, {domNodeToHTMLString} from 'react-native-render-html';
function customRenderer(htmlAttribs, children, inlineStyle, passProps) {
const html = domNodeToHTMLString(passProps.domNode);
return <Custom key={passProp.key} html={html} />;
}
Version 4.x and below
To use this hack, you'll need to add “stringify-entities” to your list of dependencies. What this hack does basically, is to use the alterNode hook to add a very unconventional __rawHtml attribute to the DOM node. The attribute will be thereafter passed to the renderer function.
import * as React from 'react';
import HTML from 'react-native-render-html';
import strigifyEntities from 'stringify-entities';
import Custom from './Custom';
function renderOpeningTag(tag, attributes) {
const strAttributes = [];
Object.keys(attributes).forEach((key) => {
strAttributes.push(`${key}="${strigifyEntities(`${attributes[key]}`)}"`);
});
return `<${tag}${strAttributes.length ? ' ' : ''}${strAttributes.join(' ')}>`;
}
function nodeToRawHTML(root) {
let html = '';
if (root.type === 'tag') {
const strChildren = root.children.reduce((prev, curr) => {
return `${prev}${nodeToRawHTML(curr)}`;
}, '');
html = `${renderOpeningTag(root.name, root.attribs)}${strChildren}</${
root.name
}>`;
} else if (root.type === 'text') {
const text = strigifyEntities(root.data);
html = text;
}
return html;
}
function alterNode(node) {
if (node.type === 'tag' && node.name === 'c') {
node.attribs.__rawHtml = nodeToRawHTML(node);
}
}
const renderers = {
c: ({__rawHtml}, children, convertedCSSStyles, passProp) => {
return <Custom key={passProp.key} html={__rawHtml} />
},
};
export default function App() {
return (
<HTML
renderers={renderers}
alterNode={alterNode}
html={'<a> <b> <c meta="xyz"> Some text </c> <b> </a>'}
/>
);
}
userKisan.find returns zero element array if I replace it with imageDatadb(just to check) this work. I dont know what I am doing wrong here, related files are imported and in other page inserting of data at userKisan runs smoothly meaning code is working and I am sure data is there.
......
import {imageDatadb} from '../api/imageData';
import {userKisan} from '../api/usersdb';
render() {
console.log('docsReadyYet',this.props.imageData);
let fileCursors = this.props.imageData;
let display = fileCursors.map((img, key) => {
console.log('aaaaa',img.kUserrId);
let kdata = userKisan.find({ }).fetch(); // if i replace userKisan with imageDatadb this works
console.log('kdata',kdata);
return <div key={key}>
<img src={img.imageData}/>
{kdata}
</div>
})
return (
<div>
<PrivateHeader title= 'All'/>
{/* <image src=""/> */}
{display}
</div>
);
}
and the tracker is
export default withTracker( ( props ) => {
const filesHandle = Meteor.subscribe('All image data');
const docsReadyYet = filesHandle.ready();
const imageData = imageDatadb.find({}).fetch();
return {
docsReadyYet,
imageData,
};
})(AllCustomers);
You need a new publish with userKisan and subscribe to that. When you try to find documents from imageDatadb they are available because you subscribed to them.