Adding script within jsx - reactjs

In my jsx file I have the following function:
function GetComments({commentsArray}) {
return commentsArray.map((comment) => {
let localId = comment.LocalId;
return (
<div className="comments-container">
<div className="commenter">
<span className="id-label">{comment.LocalId}</span>
<script>
if (comment.ParentCommentId !== null) {
document.write("<span class=reply title=in reply to " + comment.LocalId + "></span>")
}
</script>
</div>
</div>
);
});
}
Now I don't get any errors but when that script tag part is in there, the page just doesn't load, it's just constantly stuck trying to load. Where have I gone wrong with adding a script to this? I am only wanting that element created when that condition is met. I have been looking online and I see that it's different in jsx for adding scripts but confused on how to correctly implement my script

There are a few problems with your code, the biggest of which is the use of document.write, which you should never be using in react code. To understand why, it's important to realize that one of the most fundamental design goals of react is to abstract any direct manipulation of the DOM away from the programmer and instead allow them to specify the way in which a lighter weight virtual DOM reacts to state changes. Try something like the following:
export function GetComments({ commentsArray }) {
return (
<>
{commentsArray.map((comment) => {
const localId = comment.LocalId;
return (
<div key={localId} className="comments-container">
<div className="commenter">
<span className="id-label">{localId}</span>
{comment.ParentCommentId && (
<span className="reply" title={`in reply to ${localId}`} />
)}
</div>
</div>
);
})}
</>
);
}
In addition to doing away with document.write, this addresses the following issues:
Consistent use of localId.
Conditional rendering to render the "reply" span when comment.ParentCommentId is truthy.
Template literals to more clearly express the "in reply to" string. This is maybe a bit more of a style issue than a functional one, but template literals are the modern way to format variables into string templates.
The use of key inside the map to "help React identify which items have changed, are added, or are removed."
Wrapping the whole map expression in a fragment so that it has a return type which is not an array, which is a requirement for it being used as a JSX element.

I have managed to actually come up with a way to solve the issue, don't know if this is the best way.
I created an additional function where I pass the id into it and do the check there.
function ParentIdCheck({id}) {
if (id !== null) {
return(
<span className="reply" title={"in reply to " + id}></span>
);
}
}
and now the original function looks like:
function GetComments({commentsArray}) {
return commentsArray.map((comment) => {
let localId = comment.LocalId;
return (
<div className="comments-container">
<div className="commenter">
<span className="id-label">{comment.LocalId}</span>
<ParentIdCheck id = {comment.ParentCommentId}/>
</div>
</div>
);
});
}
I don't know if this is the best way though, so open to suggestions on how to improve this

Related

How do I replace "br" tags in axios response with actual "br" tags?

using: React, WordPress REST API, Axios
I'm pulling results from a custom post type and when I get the results for the field "acf.other" which is a textbox field, whenever the user inputs a return, it puts either (depending on the cpt settings) a <br /> tag, </p> <p> tags, or nothing, resulting in a running paragraph w/ no formatting. If it puts the tags, then the results show the tag on the site, and doesn't actually use the tags (see img).screenshot of results
export const SingleRecent = () => {
const { singleSlug } = useParams();
const [single,setSingle] = useState([]);
useEffect(() => {
axios.get(recent + singleSlug)
.then(response => {
setSingle(response.data);
})
.catch((error) => {
console.log(error);
})
}, [singleSlug]);
return (
<><div className="pagebg singlePageBg">
<main>
<div className="container notHomeContainer singleContainer">
<div className="gallery notHomeGallery singleGallery">
{single.map(single_recent => <SingleRecentItems key={single_recent.id} single_recent={single_recent} />)}
</div>
</div>
</main>
</div></>
);
}
class SingleRecentItems extends Component {
render() {
let { acf } = this.props.single_recent;
return (
<>
<h1>{acf.title}</h1>
<h4>{acf.style}</h4>
<div className="photos">
<span className="recentPhoto">
<img
src={acf.photo}
alt={acf.title}
/>
</span>
</div>
<div className="otherInfo">
{acf.other}
</div>
<div className="location">
Photo Taken in: {acf.location_other}<br />
{acf.location_city}<br />
{acf.location_state}<br />
{acf.location_country}
</div>
<div className="keywords">
{acf.subject}
</div>
</>
)
}
}
I'd like to take the results of {acf.other} and turn the tags into functional tags so that it will format the results (more or less) the way the user intended. Can I somehow assign {acf.other} to a var, parse and concat, and return the results? Is there a better way?
The general rule is that you don't want to treat your data as code. If this were third-party data, a malicious user might write a <script src="steal-their-cookies.xyz"> into your webpage, which you'd much rather see as "<script src="steal-their-cookies.xyz">" on the page rather than as executed code! Inserting user-generated data into your page as code is a great way to expose yourself to cross-site scripting (XSS) attacks and other page-breaking and data-stealing hazards.
However, in your case, the data is supposed to be code, and is supposed to come from a trusted source. To enable your use-case, React has a dangerouslySetInnerHTML property that allows you to treat this data as code. As in this LogRocket blog, this makes sense for a CMS that has trusted editors submitting rich text, but you should be aware of the hazards and use the property with extreme caution.
<!-- Beware the caveats above. -->
<div className="otherInfo" dangerouslySetInnerHTML={{__html: acf.other}} />
Try parsing the result String to escape the HTML characters. It could look something like this:
parsedResult = response.data
.replace("\\u003c", "<")
.replace("\\u003d", "=")
.replace("\\u003e", ">")
.replace("\\u0026", "&");
It might be the case you'll need to escape more characters. A quick lookup will give you the needed codes.
Hope this helps!

Unable to render image based off prop from axios call in React web app

I'm attempting to render an image using template literals, with the result of a database query used in the src of an image.
However, the template literal is being transformed into a pure string. It feels like a basic problem, but I can't figure out what I'm doing wrong.
Here's the render function in my component:
const fetchLoans = this.state.debts.map(debt => {
return (<div className="individual-loan-amount-outstanding" key={debt._id}>
<img className="individual-loan-picture" src={('../../../images/${debt.provider}.png')} alt="Provider of loan."/>
</div>
)
})
Can anyone point out where I've gone wrong? Thanks!
It could be because you aren't using backticks in the template?
<img
className="individual-loan-picture"
src={`../../../images/${debt.provider}.png`}
alt="Provider of loan."
/>
Replace ' with `:
const fetchLoans = this.state.debts.map((debt) => {
return (
<div className="individual-loan-amount-outstanding" key={debt._id}>
<img className="individual-loan-picture" src={`../../../images/${debt.provider}.png`} alt="Provider of loan." />
</div>
);
});

Newbie to React : Any help would be greatly appreciated

I am doing a code challenge and am very new to React. I am used to hard coding things, and I need to loop in data from a JS file. I am familiar with HTML and CSS so styling will be no problem, but how I can pull in the data from the menu items without hardcoding. Below are links to what I have in the project files as well as a reference as to what it should look like. Thanks!
[1]: https://i.stack.imgur.com/JtE4X.png
[2]: https://i.stack.imgur.com/n412K.png
[3]: https://i.stack.imgur.com/ZVSl2.png
Here's a simplified example
Interactive sandbox
https://codesandbox.io/s/lucid-lovelace-kr6rr?fontsize=14&hidenavigation=1&theme=dark
Here's the code:
function myComponent() {
// Here's your data
// You'll probably want to set this to state after fetching it
// (It doesn't need to be in a const)
const myList = [
"https://i.stack.imgur.com/JtE4X.png",
"https://i.stack.imgur.com/n412K.png",
"https://i.stack.imgur.com/ZVSl2.png"
];
// map over your data
// here we are rendering an html img for each item
// and we give each a key to help react keep a record of what is has rendered
return (
<div>
{myList.map(url => <img src={url} key={url} />)}
</div>
)
}
Hope it helps!
On line 19, try
let menuItems = this.state.menuItems.map((item) => {
<MenuItem name={item.name} img={item.imageName} price={item.details.price} />
}
Then in your return,
<div id="combo-header">
<h1>Combo Meals</h1>
{menuItems}
</div>
You can also choose to loop within your render instead of assigning to its own menuItem variable.
<div id="combo-header">
<h1>Combo Meals</h1>
{this.state.menuItems.map((item) => {
<MenuItem name={item.name} img={item.imageName} price={item.details.price} />
}}
</div>
Note that the second option requires the javascript to be wrapped in {} so that you can write javascript directly in the JSX.
After this, you'd write a MenuItem component that would intake the props above.
You will also have to perform a check that the array is not empty before mapping over it, or there will be errors.

Can only set one of `children` or `props.dangerouslySetInnerHTML`

I'm trying to set the inner HTML of an alert div, but receiving the error message : Can only set one of 'children' or props.dangerouslySetInnerHTML'.
Why does this happen ?
function alertContent(alert) { return {__html: alert.text} }
const Alerts = ({ alerts=[{level: 'warning', text:'<p>Warning message!</p>'}], onDismiss }) => (
<div className="alerts">
{alerts.map(alert =>
<Alert
bsStyle={alert.level}
key={alert.id}
onDismiss={onDismiss}
dangerouslySetInnerHTML={alertContent(alert)}
></Alert>
)}
</div>
)
I had this error message in a react application.
My issue was I had code as below
<p dangerouslySetInnerHTML={{ __html:stringContainingHtml}}> </p>
My issue was the space in the <p> </p> tags - since html gets injected inside these tags the space was causing an issue as it wasn't empty.
Hope this might help some people.
This happens because dangerouslySetInnerHTML can only be applied to one element. In your case, <Alert/> is a complex element made up of multiple children. That being said, the BS Alert component does accept HTML:
<Alert bsStyle="warning">
<strong>Holy guacamole!</strong> Best check yo self, you're not looking too good.
</Alert>
or
<Alert bsStyle="warning">
<span dangerouslySetInnerHTML={alertContent(alert)} />
</Alert>
Solution, You have to make a separate component to render element in which you are using dangerously set.....
For Example:
const RenderHTML = (props) => (<span dangerouslySetInnerHTML={{__html:props.HTML}}></span>)
YourData.map((d,i) => {
return <RenderHTML HTML={d.YOUR_HTML} />
})
you have to close the html tag short hand if there aren't inner HTML
<Alert dang...={} />
instead <Alert></Alert>
I have solved this issue myself.
When you are recursively rendering a component, you must separate out the part of the component where dangerouslySetInnerHTML is being used into another component and then use that component in the one you are recursively rendering.

React: Invariant Violation (without table)

I have looked some answer in stackoveflow. But most of them have something todo with <table> and <tbody>. But my problem is nothing to do with that.
I render this <GISMapDropdownMenu> component using ScriptjsLoader of react-google-maps library.
initialCustomSetup: function() {
let GISMapDropdownMenu = this.refs.cornermenu;
if (googleMapInstance.props.map.controls[google.maps.ControlPosition.TOP_LEFT].j.length === 0) {// push only once
googleMapInstance.props.map.controls[google.maps.ControlPosition.TOP_LEFT].push(GISMapDropdownMenu);
GISMapDropdownMenu.style.zIndex = 1;
GISMapDropdownMenu.style.display = 'inline-block';
}
},
render: function() {
return(
<ScriptjsLoader
**** some setup ****
googleMapElement={
<GoogleMap
ref={googleMap => {
if (googleMap) {
googleMapInstance = googleMap;
layerType = self.props.layerType;
self.initialCustomSetup();
}
return;
}} >
<GISMapDropdownMenu
ref="cornermenu"
availableDesa={self.props.availableDesa}
availableDesaColor={self.props.availableDesaColor} />
{self.props.children}
</GoogleMap>
}
/>);
Here is the implementation of GISMapDropdownMenu.
<div className="gmnoprint GISMap-dropdownmenu" style={{display: 'none'}}>
<div>
<div ref="icon" className="GISMap-dropdownmenu--icon" onClick={this.handleIconClick}>
<img src={BurgerIcon} draggable="false" />
</div>
<div ref="content" className="GISMap-dropdownmenu--content" style={{display: 'none'}}>
<div className="GISMap-dropdownmenu--content_header">
<div className="GISMap-dropdownmenu--content_header__title">List of Desa ({number_of_desa})</div>
<div className="GISMap-dropdownmenu--content_header__cross" onClick={this.handleCrossCLick}>
<img src={CrossIcon} draggable="false" />
</div>
</div>
<div className='GISMap-dropdownmenu--content_list'>
{array_div_element_inrange_assigned_desa}
{array_div_element_inrange_desa}
{array_div_element_assigned_desa}
</div>
</div>
</div>
</div>
{array_div_element_something} is array of this.
<div key={"desa_name-"+desa.desa_name} className={"GISMap-dropdownmenu--content_list__"+desa_color.status}>Desa {desa.desa_name}</div>;
I got this error when use <GISMapDropdownMenu /> component.
Uncaught Error: Invariant Violation: processUpdates(): Unable to find child 97 of element. This probably means the DOM was unexpectedly mutated (e.g., by the browser),
I realised something:
I move the DOM of <GISMapDropdownMenu /> component after mounted. Because I need it to be in the top left of google map.
Any suggestion?
Potential problems: First off, these are guesses as I don't do React all the time, but having said that, here are some possible causes of this:
1) Unmounting components:
This probably means that setState is being called in a callback that was firing after the component that initially started the request has already been unmounted from the dom.
More details on this type of error
2) Having incomplete HTML tags in your markup
For example, adding a <p> tag without the closing </p>. The browser likes all html tags to be complete, so if it doesn't see an end tag for something, it'll add it. Try getting the HTML of your resulting component and comparing it to what you expect. Also remove the child components bit by bit (e.g. remove ) to see when it starts to work so you know what causes this problem. Your html from what I can see doesn't cause this.
3) Build your child components before rendering
Maybe you need to do as this similar answer does
4) Changing the DOM using something other than React
Is your page using jquery or something to change this component in anyway? ie bind events or add/remove anything? If so, this can be causing that as well. Try disabling this and see if it solves the problem
If none of the above help, I'd suggest adding a JSFiddle so people can see what the problem is for sure.

Resources