className not working on dangerouslySetInnerHTML - reactjs

I've initial string like this:-
My first name is #John# and last name is %Smith#.
Where I'll replace:-
# with <span className="contentDescHighlighted">
% with <span className="contentDescHighlighted content_bold">
# with </span>
the working code is as follow:-
const handleAddingHighlight = (data) => {
let changeAT = data.replaceAll(`#`, `<span className="contentDescHighlighted">`)
let changePercentage = changeAT.replaceAll(`%`, `<span className="contentDescHighlighted content_bold">`)
let highlightedData = changePercentage.replaceAll(`#`, `</span>`); console.log(highlightedData)
return highlightedData
}
After changing the string, then I'll inject them using dangerouslySetInnerHTML as follow:-
<p
className="contentDesc"
dangerouslySetInnerHTML={{__html: handleAddingHighlight(`My first name is #John# and last name is %Smith#.`)}}
></p>
Unfortunately, the styling/className applied didn't take any effect at all as shown below:-
// what it should looks like
<p className="contentDesc">
My first name is <span className="contentDescHighlighted">John</span> and last name is <span className="contentDescHighlighted content_bold">Smith</span>.
</p>
// current outcome when using dangerouslySetInnerHTML
<p
className="contentDesc"
dangerouslySetInnerHTML={{__html: handleAddingHighlight(`My first name is #John# and last name is %Smith#.`)}}
></p>
Complete component will look like this
export default function Test() {
const handleAddingHighlight = (data) => {
let changeAT = data.replaceAll(`#`, `<span className="contentDescHighlighted">`)
let changePercentage = changeAT.replaceAll(`%`, `<span className="contentDescHighlighted content_bold">`)
let highlightedData = changePercentage.replaceAll(`#`, `</span>`); console.log(highlightedData)
return highlightedData
}
return (
<>
{/* what it should looks like */}
<p className="contentDesc">
My first name is <span className="contentDescHighlighted">John</span> and last name is <span className="contentDescHighlighted content_bold">Smith</span>.
</p>
{/* current outcome when using dangerouslySetInnerHTML */}
<p
className="contentDesc"
dangerouslySetInnerHTML={{__html: handleAddingHighlight(`My first name is #John# and last name is %Smith#.`)}}
></p>
</>
)
}

This is the issue. content rendered using dangerouslySetInnerHTML property must be HTML elements. They aren't JSX at all.
It should be
class
(which is HTML attribute)
instead of
className
(which is JSX attribute)

Related

How do I pass a certain data onClick in React in this situation

Basically I have a loop that runs through "ScannedCrops" and each scannedcrop renders with a button which says "View More". I want to pass the id (Which is stored in ScannedCrop._id) and console.log that onClick.
This is the loop
<div className={styles.scannedCropsGrid}>
{
scannedCrops.map(ScannedCrop => {
return <div>
<div className={styles.center}>
<div className={styles.scannedCropsContent}>
<img src={ScannedCrop.image} alt="/" />
<div className={`${ScannedCrop.healthyBoolean ? styles.healthyPlant : styles.notHealthy}`}>
{
<p>{ScannedCrop.healthyBoolean ? "Healthy" : "Not Healthy"}</p>
}
</div>
<div className={styles.healthDesc}>
<p>{ScannedCrop.healthyBoolean ? "No Disease" : ScannedCrop.diseaseName}</p>
<p>||</p>
<p>{ScannedCrop.dateTime}</p>
</div>
<div className={styles.center}>
<button className={styles.viewMoreBtn}>
More Info
</button>
</div>
</div>
</div>
</div>
})
}
</div>
This is the onClick function in the same component.
const getId = () => {
}
What I want to do is that onClick I want to get the ScannedCrop._id and console.log it. I am kind of a beginner at this.
First of all you want to add a 'key' attribute to the parent in your map function. This can be either index or the ScannedCrop._id (if the later is always unique).
So it'll look like this:
...
{scannedCrops.map((ScannedCrop, index) => {
return <div key={index (or ScannedCrop._id if unique)} >
...
Lastly to answer your question, you first need to define your handler function and then pass it on to your button element inside the OnClick event. It should end up looking like this:
//it is a common practice to name your functions
//'handleClick', 'handleOnView' and so on. You can name it
//whatever you want
const handleGetId = (id) => console.log(id)
...
<div className={styles.center}>
<button
onClick={() => handleGetId(ScannedCrop._id)}
className={styles.viewMoreBtn}>
More Info
</button>
</div>
...
Hope that answers your question!

how to add dynamic content inside a textarea tag using map method

i am currently working on chat application with sockets , when i get different messages i use an array and then use map method to display them in simple html tags like p etc it worked perfect but inside text-area its not working i also tried to set text-area value property with map method but only i see is [object object] . also how can i automatically move the scroll bar down when messages there are more messages.
here is the code
import { Fragment, useEffect } from "react";
import { format } from "date-fns";
const Chat = ({ name, message }) => {
const date = new Date();
const hour = date.getHours();
const minute = date.getMinutes();
const second = date.getSeconds();
console.log("so bteay", message);
return (
<Fragment>
<div>
<h3 className="d-inline-block me-3"> Chat log </h3>
{name && (
<span className="me-3 d-inline-block">
<div
class="spinner-grow spinner-grow-sm text-success"
style={{ fontSize: "10px" }}
role="status"
>
<span class="visually-hidden">Loading...</span>
</div>
</span>
)}
<small className="text-muted d-block "> {name}</small>
<textarea
cols="70"
rows="8"
value={message.map((eachMsg) => {
return (
<Fragment>
{
<small className="text-muted d-inline-block">{`${hour}:${minute}:${second}`}</small>
}
<p
className="d-block shadow p-1 fw-bold rounded text-success"
style={{ fontFamily: "cursive" }}
>
{eachMsg}
</p>
</Fragment>
);
})}
></textarea>
</div>
</Fragment>
);
};
export default Chat;
You can only pass 1 child ( text in this case ) to text area. But you are trying to pass an array. If what you meant to do is to have as many as textareas as your array, this is how you should go about it:
const texts = ["Hi", "Bye","done"];
<div>
{texts.map((text) => (
<textarea>text</textarea>
))}
</div>
but if you are trying to have 1 textarea with all your texts inside it, first you need to create a long string using join method, and then render that string.
I think that you can't set html code inside textarea, unless you want to show it as a text?

How to add color to a specific part of a string in React using replace?

I'd like to make all the numbers in a string red and then render it with React.
Here's what I'm trying to do (I've made an app using create-react-app and replaced the contents of App.js with my own):
import React, { Component } from 'react';
import './App.css';
class App extends Component {
render() {
const str = 'foo123bar';
const strColor =
str.replace(/\d+/, match => <span style={{color: 'red'}}> {match} </span> );
return (
<div className="App">
{strColor}
</div>
);
}
}
export default App;
As a result only the line foo[object Object]bar was rendered in the viewport.
So how should inline styling be added to JSX?
I was able to solve this by using 'dangerouslySetInnerHTML'.
class App extends React.Component {
render() {
let str = 'foo123bar';
const strColor = str.replace(/\d+/, match => `<span style="color: red">${match} </span>` );
return (
<div className="App"
dangerouslySetInnerHTML={{__html:
strColor}}>
</div>
);
}
}
"So how should inline styling be added to JSX?"
To answer to your stated question, your line:
const strColor = str.replace(/\d+/, match => <span style={{color: 'red'}}> {match} </span> );
is returning a string - so the object statement style={{color: 'red'}}>
will not be escaped.
Add the inline styling with a string definition instead, reference the double quotes and removed curly braces:
<span style="color: red"> {match} </span>
You can add more styles by separating the key: value pairs with commas:
<span style="color: red, text-decoration: underline"> {match} </span>
Note that this will not work
The question that you're really trying to answer is how to replace parts of a string, with a component (in this case a <span> tag with styling. This issue is documented in the react github issues page, note that there are many options without requiring you to dangerously set your inner HTML as noted in several previous answers: https://github.com/facebook/react/issues/3386
You can't insert HTML into a string for it to be rendered in React, this exists as a protection from XSS.
What you can do in a case like this is something like:
const str = 'foo123bar';
const coloredStr = str.match(/\d+/);
const [before, after] = str.split(/\d+/)
return (
<div className="App">
{before}
{coloredStr && coloredStr[0] &&
<span style="color: red">{coloredStr[0]}</span>
}
{after}
</div>
);
For a more complex example you will need more complex logic. E.g. multiple parts can be styled - you can find all the matches and the non matching parts and put them in a list in the right order with an indicator should you use the span or not. Something like:
list.map((elem) => elem.isColored ? <span style="color: red">{elem.value}</span> : elem.value)
EDIT
As mentioned in the comments, here is an implementation for multiple elements:
const str = 'foo123bar456baz897ban';
let strCopy = str;
const list = [];
while(strCopy) {
const text = strCopy.split(/\d+/, 1)[0];
list.push(text);
const match = strCopy.match(/\d+/);
if (!match) {
break;
}
list.push(match[0]);
strCopy = strCopy.substring(match.index + match[0].length);
}
return (
<div className="App">
{list.map((elem, index) => index % 2 === 0
? elem
: <span style="color: red">{elem}</span>
)}
</div>
);

How to check if profile images exist else use fallback jpg in a react App

I am displaying search results in a React App. The file img src={/users/${id}/avatar.jpg} does not exist for all users. The user's gender is available in the field $gender. If they do not have an avatar.jog uploaded, I want to use male.jpg for gender=male and female.jpg for gender=female.
Below is the component code:
import React, { Component } from 'react'
import AppLink from '../../others/link/link'
class SearchResult extends Component {
</span>
</div>
<div className="mb1">
<span><span className="text-bold">Gender: src here</span>
</div>
</div>
</div>
</div>
)
}
}
export default SearchResult
The logic for determining a user's profile images might be better placed in your backend, so that your overall design for fetching and displaying avatar images is simpler an more robust.
If a user has not uploaded an avatar image, the server can decied what image to serve for that user by the user's id (ie to serve an avatar if one has been uploaded, or serve a gender image as a fallback). That saves potential issues in your front end code (ie valid client side user data, but no avatar image on server, causing image not found error).
If however, you know that an avatar exists when say, the users id is defined, then the following might might your requirements:
class SearchResult extends Component {
render() {
let {
id,
username,
firstname,
surname,
gender
} = this.props
// Determine the avatarImage path depending on the existance of a valid
// user id. If user id is invalid, fallback to an image based on gender
const avatarImage = (id !== undefined && id !== null) ?
`/users/${id}/avatar.jpg` : `/${ gender }.jpg`
return (
<div className="user_item">
<div className="user-container">
<div className="user-l">
<div className="text-center">
<AppLink url={`/profile/${username}`}>
{/* Apply the computed avatarImage to the img element /*}
<img src={ avatarImage } className="user_avatar" />
</AppLink>
</div>
</div>
<div className="user-m half-container">
<div className="halfL">
<div className="mb1">
<AppLink
url={`/profile/${username}`}
className="lead m2 s_userName"
label={username}
/>
</div>
<span>{firstname} {surname}</span>
</div>
<div className="halfR">
<div className="mb1">
<span>
<span className="text-bold">Age:</span> {age}
</span>
</div>
<div className="mb1">
<span><span className="text-bold">Gender:</span> {gender}</span>
</div>
</div>
</div>
<div className="mb1">
<AppLink
url={`/profile/${username}`}
className="btn btn-sm"
label={`Contact ${username}`}
/>
</div>
</div>
</div>
</div>
)
}
}
export default SearchResult
This answer is not an ideal solution (as identified above, you should be storing the user's avatar state in your database), however it shows a strategy to resolve the user avatar from the client in order to achieve what you're after.
In short, this approach will issue a request to your back-end to see if the user's avatar image exists on the server. If the request is successful, that avatar image url is stored in the component and used for subsequent rendering.
If the request fails, the component applies the "fallback" behaviour - that is, it will use the input props to deduce the "gender" image, similar to that shown above.
See [UPDATED] tags below for relevant additions from your current code:
class SearchResult extends Component {
constructor(props) {
super(props)
/* [UPDATED] Setup the default userImage to be initially displayed */
this.state = {
userImage : `/images/users.jpg`
}
}
/* [UPDATED] When the component is mounted, use the id and gender props
to decide which image to display for the user
*/
componentDidMount() {
const { id, gender } = this.props
const avatarImage = `/users/${id}/avatar.jpg`
/* [UPDATED] Query the server for the user's avatar - if the query
succeeds then use the avatar image, otherwise fallback to gender
image. In either case, the userImage is stored in the components
state
*/
fetch(avatarImage).then(response => {
if(!response.ok) {
this.setState({ userImage : `/${ gender }.jpg` })
}
else {
this.setState({ userImage : `/users/${id}/avatar.jpg` })
}
})
}
render() {
let {
id,
username,
firstname,
surname,
gender
} = this.props
{/* [UPDATED] Extract userImage from state /*}
const { userImage } = this.state
return (
<div className="user_item">
<div className="user-container">
<div className="user-l">
<div className="text-center">
<AppLink url={`/profile/${username}`}>
{/* [UPDATED] Apply the userImage to the img element based on state /*}
<img src={ userImage } className="user_avatar" />
</AppLink>
</div>
</div>
<div className="user-m half-container">
<div className="halfL">
<div className="mb1">
<AppLink
url={`/profile/${username}`}
className="lead m2 s_userName"
label={username}
/>
</div>
<span>{firstname} {surname}</span>
</div>
<div className="halfR">
<div className="mb1">
<span>
<span className="text-bold">Age:</span> {age}
</span>
</div>
<div className="mb1">
<span><span className="text-bold">Gender:</span> {gender}</span>
</div>
</div>
</div>
<div className="mb1">
<AppLink
url={`/profile/${username}`}
className="btn btn-sm"
label={`Contact ${username}`}
/>
</div>
</div>
</div>
</div>
)
}
}
export default SearchResult
Hope this helps!

String Template in React JSX

I created a server and inserted some data in it, Now when I want to refer to that data, it's not displaying the name field, I used string templates to read the name field from the server and show it, but it's not working, is there something wrong with syntax?
this is my component in React :
const Rank = ({ name, entries }) => {
return(
<div>
<div className='white f3'>
{` ${name} , your current rank is ... `}
</div>
<div className='white f1'>
{entries}
</div>
</div>
);
}
{entries} displays right,but i dont receive any data for {name}.
Please note that string literals are part of ES6. If you've not configured the project correctly, the string literals will not work.
Try hoisting he string concatenation before return and use it directly in the JSX as:
const rankOutput = name + ', your rank ...';
And use it in JSX as:
{rankOutput}
So final code would look like this:
const Rank = ({ name, entries }) => {
const rankOutput = name + ', your current rank is ...';
return(
<div>
<div className='white f3'>
{rankOutput}
</div>
<div className='white f1'>
{entries}
</div>
</div>
);
}

Resources