Render raw HTML inside a react div - html

I have this raw HTML that is stored in the database and I'm trying to render it inside a div.
const style = {
border: '2px solid #eee',
height: '270px',
width: '100%',
overflow: 'auto',
padding: '10px'
}
const test = () => {
const [contentFromDB, setContentFromDB] = useState("");
useEffect(() => {
async function fetchData() {
setContentFromDB(await myAPI.getData())
}
}, []);
return (
<>
<div className="row">
<h3>Test Render</h3>
<div style={style}>
{contentFromDB}
</div>
</div>
</>
)
}
export default test;
The problem is that it is keeping all the HTML tags, it is rendering as a string (image below). Is there a way to render the raw HTML inside my div?

You can use dangerouslySetInnerHTML to achieve this.
const style = {
border: '2px solid #eee',
height: '270px',
width: '100%',
overflow: 'auto',
padding: '10px'
}
const test = () => {
const [contentFromDB, setContentFromDB] = useState("");
useEffect(() => {
async function fetchData() {
setContentFromDB(await myAPI.getData())
}
}, []);
return (
<>
<div className="row">
<h3>Test Render</h3>
<div style={style}>
<div dangerouslySetInnerHTML={{__html:contentFromDB}} />
</div>
</div>
</>
)
}
export default test;

Related

Styled-components: Style not updated after switching from mobile

Here:
Open the app in desktop width, notice the div has green background
Reduce browser width to mobile, the div background should change to gray
Again, increase browser width to desktop, notice the gray background remains, instead of green
What should have happened
The background in last step should be green as in the first step, isn't it?
Logging value of isMobile does seem to show it is being updated.
Here is also code:
import React from 'react';
import styled from 'styled-components';
import { useMediaQuery } from 'react-responsive';
let MenuItem = styled.div`
height: 100px;
width: 100px;
border:1px solid red;
background-color: green;
// Select those items which are children of .submenu
.submenu & {
background-color: ${({ isMobile }) => {
return isMobile && 'lightgray';
}};
}
`;
function App() {
const isMobile = useMediaQuery({ query: '(max-width: 524px)' });
return (
<div>
<div className="submenu">
<MenuItem isMobile={isMobile}>test</MenuItem>
</div>
</div>
);
}
export default App;
import React, {useEffect, useState} from 'react';
import styled from 'styled-components';
import {useMediaQuery} from 'react-responsive';
let MenuItem = styled.div`
height: 100px;
width: 100px;
border:1px solid red;
background-color: green;
`;
function App() {
const isMobile = useMediaQuery({query: '(max-width: 524px)'});
const [color, setColor] = useState('green');
useEffect(() => {
if (isMobile) setColor('silver');
else setColor('green');
}, [isMobile])
return (
<div>
<div className="submenu">
<MenuItem style={{background: color}} isMobile={isMobile}>test</MenuItem>
</div>
</div>
);
}
export default App;
You could re-write this as:
const MenuItem = styled.div`
height: 100px;
width: 100px;
border:1px solid red;
background-color: green;
`;
const SubMenu = styled.div`
${MenuItem} {
background-color: ${({ isMobile }) => (isMobile ? `red` : 'lightgray')};
}
`;
function App() {
const isMobile = useMediaQuery({ query: '(max-width: 524px)' });
return (
<>
<SubMenu isMobile={isMobile}>
<MenuItem>MenuItem in SubMenu</MenuItem>
</SubMenu>
<MenuItem>MenuItem</MenuItem>
</>
);
}
Stackblitz
It is the correct answer:
https://stackblitz.com/edit/react-t7gqwx?file=src%2FApp.js,src%2Findex.js
You shouldn't use .submenu &.

Conditional rendering of HTML elements based on boolean value React

building a component that takes a value (attribute.readonly), and only displays the first 40 characters, unless the <Chevron onClick is triggered which means I then want to show fullDescription as opposed to the shortHeading - ideally this would render similar to how it does currently only doubling the height of the Accordion for example, to allow the rest of the content to fit. I know I'm still a bit off but some pointers for my next steps/ ideas for improving what I have already would be really appreciated!
// #flow
import styled from "styled-components";
import chevron from "../Assets/chevron-icon.svg";
type Props = { attribute: AttributeType, className?: string };
const Accordion = styled.div`
background-color: #e5e9eb;
height: 56px;
width: 612px;
border-radius: 2px;
border: 1px solid #27282a;
margin-bottom: 48px;
display: flex;
align-items: center;
span {
padding-left: 24px;
}
`;
const Chevron = styled.img`
height: 40px;
width: 40px;
float: right;
margin-right: 12px;
`;
const ExpandableString = ({ attribute, className }: Props) => {
const fullDescription = attribute.readonlyvalue;
const shortHeading = fullDescription.substring(0, 40) + '...';
const isExpanded = false;
function toggleContent() {
isExpanded = true;
}
return (
<Accordion className={className}>
<span>{shortHeading}</span>
<Chevron onClick={toggleContent} src={chevron} alt="Expand or collapse content" />
</Accordion>
);
};
export default ExpandableString;
Put your data in the state and toggle it
const ExpandableString = ({ attribute, className }: Props) => {
const [isExpanded, setIsExpanded] = React.useState(false);
function toggleContent() {
setIsExpanded(prev => !prev);
}
return (
<Accordion className={className}>
<span>{shortHeading}</span>
<Chevron onClick={toggleContent} src={chevron} alt="Expand or collapse content" />
</Accordion>
);
};
Then, according to the isExpanded state, you could conditionally render your React component.

Make element's height adaptative to what's inside

I want to make a grey-colored background for my list in ReactJS.
The problem is that the background seems to have a fixed height which seems to be smaller than the inside's height...
I never met this problem before, and the only way I found to have the background size bigger than the element's is to put height: XXXpx, which is not suitable...
Can someone explain me what I did wrong ?
Here is my class file :
import React, { Component } from "react";
import { ListGroupItem } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap";
import { API } from "aws-amplify";
import "./ProjectList.css";
export default class ProjectList extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
projects: [],
reducedView: true,
projectsNumber: 1000,
numberOfElementsToDisplay: 1000
};
}
async componentDidMount() {
try {
const projects = await this.projects();
console.log(projects)
this.setState({ projects });
} catch (e) {
alert(e);
}
this.setState({ isLoading: false });
}
projects() {
return API.get("economics-endpoint", "/list");
}
handleClick(e) {
this.setState({ reducedView: false });
e.preventDefault();
}
renderNotesList(projects) {
return [{}].concat(projects).map(
(note, i) => {
if(i !== 0){
if((this.state.reducedView && i <= this.state.numberOfElementsToDisplay) || !this.state.reducedView){
return(
<div className="element">
<ListGroupItem className="mainContainer">
<div>
ProjectName#{i}
</div>
</ListGroupItem>
</div>
);
}
}
else{
if(this.state.projectsNumber === 0){
return(
<div className="element">
<LinkContainer to={`/econx/new`}>
<ListGroupItem className="new">
<div>
+ Create New Project
</div>
</ListGroupItem>
</LinkContainer>
<div className="errorMessage">
You don't have any existing project yet.
</div>
</div>
);
}
else{
return(
<div className="element">
<LinkContainer to={`/econx/new`}>
<ListGroupItem className="new">
<div>
+ Create New Project
</div>
</ListGroupItem>
</LinkContainer>
</div>
);
}
}
}
);
}
render() {
return (
<div className="ProjectList">
<div className="projects">
{!this.state.isLoading &&
this.renderNotesList(this.state.projects)}
</div>
</div>
);
}
}
And my css file :
.ProjectList .notes h4 {
font-family: "Open Sans", sans-serif;
font-weight: 600;
overflow: hidden;
line-height: 1.5;
white-space: nowrap;
text-overflow: ellipsis;
}
.projects{
background-color: #E0E0E0;
border-radius: 10px;
padding: 1%;
}
.list-group-item{
margin-bottom: 3px;
padding: 0px;
line-height: 200px;
text-align: center;
vertical-align: middle;
border-radius: 6px;
padding-bottom: 50px;
}
.list-group-item:first-child{
padding: 0px;
border-radius: 6px;
}
.list-group-item:hover{
vertical-align: baseline;
}
.element{
width: 20%;
float: left;
padding: 0% 1% 1% 1%;
}
This doesn't answer your CSS question. I did have some thoughts on the JavaScript, though. I'll offer my refactor for your consideration. Hopefully I interpreted your original logic correctly:
import React, { Component } from "react";
import { ListGroupItem } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap";
import { API } from "aws-amplify";
import "./ProjectList.css";
export default class ProjectList extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
projects: [],
reducedView: true,
projectsNumber: 1000,
numberOfElementsToDisplay: 1000
};
}
async componentDidMount() {
try {
const projects = await this.fetchProjects();
console.log(projects);
this.setState({ projects });
} catch (e) {
alert(e);
} finally {
this.setState({ isLoading: false });
}
}
fetchProjects = () => {
return API.get("economics-endpoint", "/list");
};
handleClick = e => {
e.preventDefault();
this.setState({ reducedView: false });
};
render() {
const {
isLoading,
numberOfElementsToDisplay,
projectsNumber,
reducedView
} = this.state;
const projects = reducedView
? this.state.projects.slice(0, numberOfElementsToDisplay)
: this.state.projects;
return (
<div className="ProjectList">
<div className="projects">
{!isLoading && (
<React.Fragment>
<div className="element">
<LinkContainer to={`/econx/new`}>
<ListGroupItem className="new">
<div>+ Create New Project</div>
</ListGroupItem>
</LinkContainer>
{projectsNumber === 0 && (
<div className="errorMessage">
You don't have any existing projects yet.
</div>
)}
</div>
{projects.map((project, index) => (
<div className="element">
<ListGroupItem className="mainContainer">
<div>ProjectName#{index}</div>
</ListGroupItem>
</div>;
))}
</React.Fragment>
)}
</div>
</div>
);
}
}
This fixes your off-by-one issue by hopefully capturing your intentions as I understand them:
If it's not loading...
Show a "Create New Project" item
If they don't have any projects yet, show them a message
If they do have projects, create a new element for each one
It's worth considering whether your projectsNumber could be replaced with projects.length. If so, you can probably combine (2) and (3) above to be an either-or scenario: If they don't have projects, show a message, if they do, show the projects.

I have a problem with overlapping elements in a JSX app

I created a button that adds emails inputs in a form (for invites), but at a certain number of inputs, they overlaps others elements on the page.
Here's some css :
.App {
height: 100%;
}
html,
body,
#root {
margin: 0;
height: 100%;
font-family: "Montserrat";
}
.page {
height: 80%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.content {
margin-top: 50px;
width: 400px;
}
(It's a JSX app)
Here's a screenshot of the page: https://paste.pics/ab860613d5fd6eb82dfe59894ee24bc0
Thanks for your help !
You can add a 'container' div around your input's.. From there you can use height and width props along with the overflow: 'scroll' attribute.
Something like this:
const { useState } = React;
// Create items
const NEW_INVITES = [...Array(10).keys()].map((item, index) => {
return {
id: index + 1,
value: index + 1
}
});
function InviteInput(props) {
const { value, onChange } = props;
const handleChange = e => {
onChange && onChange(e);
};
return (
<li>
<input
value={value}
onChange={handleChange}
className="form-input"
type="email"
placeholder="nom#exemple.com"
name="invites"
required
/>
</li>
);
}
function AddInviteButton(props) {
return (
<button onClick={props.onClick}>
Ajouter une autre personne // (Add another person)
</button>
);
}
function InviteForm({ onSubmit, height, width, addBorder }) {
const [nbInvites, setNbInvites] = useState(NEW_INVITES);
let containerRef;
const onAddInviteClick = () => {
let id = nbInvites.length + 1;
setNbInvites([
...nbInvites,
{
id,
value: id
}
]);
setTimeout((cr) => {
cr.scrollIntoView({ behavior: "smooth" });
}, 100, containerRef);
};
const handleChange = (event, index) => {
let newstate = [...nbInvites];
newstate[index].value = event.target.value;
setNbInvites(newstate);
};
const handleSubmit = event => {
onSubmit(event, nbInvites);
};
const invitesContainer = {
height,
width,
overflow: 'scroll',
border: addBorder === true ? '1px solid black' : '',
}
return (
<div>
<div style={invitesContainer}>
{nbInvites.map((item, index) => {
return (
<InviteInput
key={index}
value={item.value}
onChange={e => handleChange(e, index)}
/>
);
})}
<div ref={r => containerRef = r}/>
</div>
<AddInviteButton onClick={onAddInviteClick} />
<br />
<button onClick={handleSubmit}>Submit</button>
</div>
);
}
function App() {
const [formVals, setFormVals] = useState();
const doSubmit = (event, formValues) => {
setFormVals(formValues);
};
return (
<div className="page">
<h2 className="box-title">
Qui sont les eleves de cette classe ? // (Who are the students in this
class?)
</h2>
<p>
Vous pourrez toujours en ajouter par la suite // (You can always add
some later)
</p>
<InviteForm onSubmit={doSubmit} height="100px" width="200px" addBorder />
{formVals ? <pre>{JSON.stringify(formVals, null, 2)}</pre> : ""}
</div>
);
}
ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>

Is there a good way to implement a div cross which expands onHover?

I need to display a cross in react like this and each div needs to expand onHover.
It should push the other divs away or make them smaller. Does somebody know a good strategy to achieve this either with CSS or React conditional classNames?
I can only achieve on of those: The cross or the expansion and pushing away of the other divs but without the cross form.
I tired:
text-align: -webkit-center;
with
float: left;
there are tons of way to implement it. Take a look at my snippet:
https://stackblitz.com/edit/react-opu88z
JS
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
class App extends Component {
constructor() {
super();
this.state = {
hoveringEl: 1,
};
}
getWidth = (n) => {
if(this.state.hoveringEl === n){
return 70
} else {
return 10
}
}
hover = (n) => this.setState({ hoveringEl: n })
render() {
return (
<div className="container">
<div
style={{ backgroundColor: 'red', width: `${this.getWidth(1)}%` }}
onMouseEnter={() => this.hover(1)}
/>
<div
style={{ backgroundColor: 'green', width: `${this.getWidth(2)}%` }}
onMouseEnter={() => this.hover(2)}
/>
<div
style={{ backgroundColor: 'yellow', width: `${this.getWidth(3)}%` }}
onMouseEnter={() => this.hover(3)}
/>
<div
style={{ backgroundColor: 'blue', width: `${this.getWidth(4)}%` }}
onMouseEnter={() => this.hover(4)}
/>
</div>
);
}
}
render(<App />, document.getElementById('root'));
CSS
div.container {
width: 100%;
min-height: 50px;
}
div.container > div{
height: 50px;
float: left;
}