I am building an App where Serverinput is being rendered.
Now I am trying to figure out, how it´s possible to display pure HTML. Because now it is only displayed as ...., where it should show an Image.
The question is, where should one call dangerouslySetInnerHTML() here to display all HTML as requested?
The Strings are being stores in an Array(messageList) that is being mapped. Userinput is escaped, so theres no problem on that side.
let listItems = messageList.map(d => (
<div >
<p className={d.senderId + "timestamp"}>{d.time}</p>
<p className={d.senderId} key={d.idCount} ref={d.idCount}>
{" "}
{d.text}{" "}
</p>
</div>
));
let gif1 = <img className="gif" alt="" src={gif} />;
return (
<div >
<div dangerouslySetInnerHTML={__html: {listItems}} />
<ul>{listItems}</ul>
</div>
);
Thanks a lot for any help that is given.
I updated the dangerousHTML where I thought it would work. But now it throws - Syntax error: Unexpected token, expected }
You shuould have something like this for each element that you want to show the dynamic content
<div dangerouslySetInnerHTML={__html: {yourHtmlContent}} />
How I understood is that Array(messageList) contains a list of markup strings.
So you just need to join them.
const messageList = [
"<h2>Header</h2>",
"<body>This is body!</body>",
"<footer>Footer!</footer>"
];
function createMarkup() {
return { __html: messageList.join("") };
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
It'd display something like this
You need to pass a string not an object as you do here.
(as it's just an implementation of innerHTML)
{__html: {listItems}}
Full source for completeness.
import React, { Component } from "react";
import { render } from "react-dom";
import "./styles.css";
const messageList = [
"<h2>Header</h2>",
"<body>This is body!</body>",
"<footer>Footer!</footer>"
];
function createMarkup() {
return { __html: messageList.join("") };
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
class App extends Component {
constructor() {
super();
this.state = {
name: "React"
};
}
render() {
return (
<div>
<MyComponent />
</div>
);
}
}
render(<App />, document.getElementById("root"));
Working demo.
Related
I am struggling with figuring out how to implement conditional rendering in React. Basically, what I want to do is this: if there is a reviewResponse in the reviewResponses array, I no longer want to render the reviewResponseForm. I only want to render that ReviewResponse. In other words, each review can only have one response in this app.
I am not sure what I am doing wrong when trying to implement this logic. I know I need to implement some kind of conditional statement saying if the length of my reviewResponses array is greater than 0, I need to render the form. Otherwise, I need to render that reviwResponse. Every statement I have written has not worked here. Does anybody have a suggestion?
Here is my code so far:
My review cardDetails component renders my ReviewResponseBox component and passed the specific reviewId as props:
import React from "react";
import { useLocation } from "react-router-dom";
import StarRatings from "react-star-ratings";
import ReviewResponseBox from "../ReviewResponse/ReviewResponseBox";
const ReviewCardDetails = () => {
const location = useLocation();
const { review } = location?.state; // ? - optional chaining
console.log("history location details: ", location);
return (
<div key={review.id} className="card-deck">
<div className="card">
<div>
<h4 className="card-title">{review.place}</h4>
<StarRatings
rating={review.rating}
starRatedColor="gold"
starDimension="20px"
/>
<div className="card-body">{review.content}</div>
<div className="card-footer">
{review.author} - {review.published_at}
</div>
</div>
</div>
<br></br>
{/*add in conditional logic to render form if there is not a response and response if there is one*/}
<ReviewResponseBox review_id={review.id}/>
</div>
);
};
export default ReviewCardDetails;
Then eventually I want this component, ReviewResponseBox, to determine whether to render the responseform or the reviewresponse itself, if it exists already.
import React from 'react';
import ReviewResponse from './ReviewResponse';
import ReviewResponseForm from './ReviewResponseForm';
class ReviewResponseBox extends React.Component {
constructor() {
super()
this.state = {
reviewResponses: []
};
}
render () {
const reviewResponses = this.getResponses();
const reviewResponseNodes = <div className="reviewResponse-list">{reviewResponses}</div>;
return(
<div className="reviewResponse-box">
<ReviewResponseForm addResponse={this.addResponse.bind(this)}/>
<h3>Response</h3>
{reviewResponseNodes}
</div>
);
}
addResponse(review_id, author, body) {
const reviewResponse = {
review_id,
author,
body
};
this.setState({ reviewResponses: this.state.reviewResponses.concat([reviewResponse]) }); // *new array references help React stay fast, so concat works better than push here.
}
getResponses() {
return this.state.reviewResponses.map((reviewResponse) => {
return (
<ReviewResponse
author={reviewResponse.author}
body={reviewResponse.body}
review_id={this.state.review_id} />
);
});
}
}
export default ReviewResponseBox;
Here are the ReviewResponseForm and ReviewResponse components:
import React from "react";
class ReviewResponseForm extends React.Component {
render() {
return (
<form className="response-form" onSubmit={this.handleSubmit.bind(this)}>
<div className="response-form-fields">
<input placeholder="Name" required ref={(input) => this.author = input}></input><br />
<textarea placeholder="Response" rows="4" required ref={(textarea) => this.body = textarea}></textarea>
</div>
<div className="response-form-actions">
<button type="submit">Post Response</button>
</div>
</form>
);
}
handleSubmit(event) {
event.preventDefault(); // prevents page from reloading on submit
let review_id = this.review_id
let author = this.author;
let body = this.body;
this.props.addResponse(review_id, author.value, body.value);
}
}
export default ReviewResponseForm;
import React from 'react';
class ReviewResponse extends React.Component {
render () {
return(
<div className="response">
<p className="response-header">{this.props.author}</p>
<p className="response-body">- {this.props.body}</p>
<div className="response-footer">
</div>
</div>
);
}
}
export default ReviewResponse;
Any advice would be helpful, thank you.
If I understand your question correctly, you want to render ReviewResponseForm if the this.state.reviewResponses state array is empty.
Use the truthy (non-zero)/falsey (zero) array length property to conditionally render either UI element.
render () {
const reviewResponses = this.getResponses();
const reviewResponseNodes = <div className="reviewResponse-list">{reviewResponses}</div>;
return(
<div className="reviewResponse-box">
{reviewResponses.length
? (
<>
<h3>Response</h3>
{reviewResponseNodes}
</>
)
: (
<ReviewResponseForm addResponse={this.addResponse.bind(this)}/>
)}
</div>
);
}
I have a list of react-icons passed through props in a "Card Component". In my Card component's render method, I have something like this:
render() {
...
{
this.props.icons.map(icon => {
return (
<div className="icon-square">
/* What should I do here so I render the "icon" variable" */
</div>
)
})
}
}
Note: The list consists of react-icons which are React components themselves.
I tried a lot of things, but I can't quite figure out how I can render the icon. It would be awesome if someone could help me. Thank you
Let say you've passed a list of an icon like
import { FaBeer, FaBug, FaAnchor, FaCoffee } from 'react-icons/fa';
const icons = [FaBeer, FaBug, FaAnchor, FaCoffee];
ReactDOM.render(
<CardComponent icons = {icons} />,
document.querySelector('root')
};
CardComponent.js
class CardComponent extends React.Component{
...
render() {
// Icon is a Component
return (
this.props.icons.map((Icon) => {
return <Icon />
});
)
}
}
If the icon is a react component, then:
this.props.icons.map(Icon => {
return (
<div className="icon-square">
<Icon/>
</div>
)
})
Here is two difference for use your icon, If you pass as a JSX you should use {icon}
But if you pass as a component you should use like this <Icon/>
I think wrapping you need to just put icon is {}
render() {
...
{
this.props.icons.map(icon => {
return (
<div className="icon-square">
{icon}
</div>
)
})
}
}
I'm new to react and am trying to implement a simple for loop, as demonstrated in this other stackoverflow post. However, I cannot seem to make it work. I just want to run the component 5 (or any number of) times rather than map over an array or similar.
DEMO: https://stackblitz.com/edit/react-ekmvak
Take this example here:
index.js:
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
import Test from './test';
class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
render() {
return (
<div>
for (var i=0; i < 5; i++) {
<Test />
}
</div>
);
}
}
render(<App />, document.getElementById('root'));
test.js
import React from "react";
export default function Test() {
return (
<p>test</p>
);
}
Can anyone tell me where I'm going wrong? I've tried to copy the other stackoverflow post and tried test() also. I still get this error:
Error in index.js (18:27) Identifier expected.
Thanks for any help here.
You're trying to use plain Javascript in JSX. You have the right idea but your syntax is wrong. Instead, move your Javascript code (for loop) out to your render() method (above the return())
render() {
let items = []
for (let i = 0; i < 5; i++) {
items.push(<Test key={i} />)
}
return (
<div>
{items}
</div>
);
}
Few things to note here:
Components that are being iterated over, need a unique key property. In this case, we can use the current value of i
Elements can be rendered in JSX by wrapping them in curly braces, shown above. { items }
JSX will accept you any valid JavaScript expressions, Declarative vs Imperative Programming maybe this source can help you. You can create a declarative solution like those shown by the other colleagues (and the best solution), and also you can wrap your imperative solution into a function or method.
const Test = () => {
return (
<p>test</p>
);
}
class App extends React.Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
createElements = () => {
const elments = [];
for (var i=0; i < 5; i++) {
elments.push(<Test />)
}
return elements;
}
render() {
return (
<div>
{this.createElements()}
</div>
);
}
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
You need a returned value inside the JSX to be able to display anything, here's how you can do that:
const Test = () => {
return (
<p>test</p>
);
}
class App extends React.Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
render() {
return (
<div> { Array.from(Array(5)).map(el => <Test />) } </div>
);
}
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
You can't use a for loop like this in your return. I would recommend you using a map for this and looping over an array. You can do this by simply creating an array and directly mapping over it :
[...Array(totalSteps)].map(el => el {
return (
<Test />
)
})
You will have to surround this whole block in {}. This will create an array of totalSteps items and return totalSteps <Test />. So if totalSteps is 5, you'll be rendering that component 5 times. This is how your final component should look like :
render() {
return (
<div>
{[...Array(totalSteps)].map(el => el {
return (
<Test />
)
})}
</div>
);
}
For Dynamic Implementation, you can just pass an object to the parameter and display its different values in different components.
We will use map to iterate through the array of objects. Following is the example code in this regard:
return (
<>
{userData.map((data,index)=>{
return <div key={index}>
<h2>{data.first_name} {data.last_name}</h2>
<p>{data.email}</p>
</div>
})}
</>
In my scenerio, the following code helped me to generically generate multiple cards on the same page using a loop (map):
{data.map((data1, id)=> {
return <div key={id} className='c-course-container-card'>
<Courses
courseid = {data1.courseid}
courselink = {data1.courselink}
img = {data1.imgpath}
coursetitle = {data1.coursetitle}
coursedesc = {data1.coursedesc}
/>
</div>
})}
Hope it helps! :)
I'm using the following code which is being parsed by the linter eslint-plugin-react. It returns the warning:
"product is missing in props validation"
while i declare product in propTypes at the bottom and that i pass it to function. any idea ?
import React from 'react'
const ProductDesc = (props)=>({
render(){
return (
<div>
<h1>{props.product.headline}</h1>
<img src={props.product.images[0].imagesUrls.entry[2].url} alt="Thumbnail large pic"/>
<p>Yeah</p>
</div>
)
}
})
ProductDesc.propTypes = {
product: React.PropTypes.object
};
export default ProductDesc;
syntax should have been
const ProductDesc = (props)=>{
return (
<div>
<h1>{props.product.headline}</h1>
<p>Yeah</p>
</div>
)
}
I am creating a basic blog in react using Flux + React Router + Firebase. I am having trouble trying to get a single blog post to render. When I click on the link to a single post, I try to filter out all of the other posts from a list of all posts and display only a single post from my firebase database.
I attempt to do this by matching the key of the firebase entry with the url params like so if (this.props.routeParams.key===key) . I really do not know what I have to do to make this happen. Any suggestions are welcome.
Below is Blogger.jsx, the page where I allow a user to create a blog post and then beneath the blog post, I display a list of the titles all blog posts.
import AltContainer from 'alt-container';
import React from 'react';
import { Link } from 'react-router';
import List from './List.jsx'
import Firebase from 'firebase'
import BlogStore from '../stores/BlogStore'
import BlogActions from '../actions/BlogActions';
const rootURL = 'https://incandescent-fire-6143.firebaseio.com/';
export default class Blogger extends React.Component {
constructor(props) {
super(props);
BlogStore.getState();
BlogStore.mountFirebase();
{console.log(this.props.location.query)}
};
componentDidMount() {
BlogStore.listen((state) => {
this.setState(state)
})
this.firebaseRef = new Firebase(rootURL + 'items/');
}
componentWillMount() {
BlogStore.unlisten((state) => {
this.setState(state)
})
}
renderList = (key) => {
return (
<Link to={`blogshow/${key}`}> <List key={key} blog={this.state.blog[key]} /> </Link>
)
}
handleInputChange = () => {
BlogStore.setState({
title: this.refs.title.value,
text: this.refs.text.value});
}
handleClick = () => {
BlogStore.handleClick();
}
render() {
return (
<div>
<div className="row panel panel-default">
<div className="col-md-8 col-md-offset-2">
<h2>
Create a New Blog Post
</h2>
</div>
</div>
<h2>Blog Title</h2>
<div className="input-group">
<input
ref="title"
value={BlogStore.state.title}
onChange = {this.handleInputChange}
type="text"
className="form-control"/>
<span className="input-group-btn">
</span>
</div>
<h2>Blog Entry</h2>
<div className="input-group">
<textarea
ref="text"
value={BlogStore.state.text}
onChange = {this.handleInputChange}
type="text"
className="form-control"/>
</div>
<div className="blog-submit input-group-btn">
<button onClick={this.handleClick}
className="btn btn-default" type="button">
Publish Blog Post
</button>
</div>
{/*<List blog={this.state.blog} />*/}
{Object.keys(BlogStore.state.blog)
.map(this.renderList)}
</div>
);
}
}
When a user clicks on a link to a single blog post, they should be transported to a page which shows only that single blog post. I have called this component BlogShow. I can't get BlogShow to render because I keep on getting the error
invariant.js?4599:45 Uncaught Invariant Violation: BlogShow.render(): A
valid React element (or null) must be returned. You may have returned
undefined, an array or some other invalid object.
This is BlogShow.jsx:
import AltContainer from 'alt-container';
import React from 'react';
import { Link } from 'react-router';
import Blogger from './Blogger'
import List from './List'
const rootURL = 'https://incandescent-fire-6143.firebaseio.com/';
import BlogStore from '../stores/BlogStore'
import BlogActions from '../actions/BlogActions';
export default class BlogShow extends React.Component {
constructor(props) {
super(props);
{console.log(this.props.routeParams.key)}
this.filterList = this.filterList.bind(this);
}
filterList(key) {
if (this.props.routeParams.key===key) {
return (<List key={key} blog={BlogStore.state.blog[key]} />)
}
}
render() {
<div> {Object.keys(BlogStore.state.blog).map(this.filterList)} </div>
}
}
You are getting that error because your Component BlogShow is not returning anything.
render() {
<div> {Object.keys(BlogStore.state.blog).map(this.filterList)} </div>
}
Should be:
render() {
return <div> {Object.keys(BlogStore.state.blog).map(this.filterList)} </div>
}
I'm not familiar with React.js at all, but I am familiar with pure JS arrays. To remove elements from an array, you should use .filter(), and then afterwards you can map the items.
Something like this:
filterList(key) {
return this.props.routeParams.key === key; // true if the item should stay in the list
}
mapList(key) {
return <List key={key} blog={BlogStore.state.blog[key]} />;
}
render() {
return <div> {Object.keys(BlogStore.state.blog).filter(this.filterList).map(this.mapList)} </div>;
}