I am trying to create a email in React using the MJML email library. It runs off react and I have it all working but I need to render 2 sections rather than 1. When I render 1 it doesn't appear properly on the webpage as I need them to be different sizes.
When I try and wrap the elements within a array the return become null, take out one of the sections and it gets returned.
Any help would be appreciated, here is the code.
render() {
const { mjAttribute } = this.props
const content = [this.renderEmailOverhead()]
const innerContent = [this.renderEmailBanner(), this.renderEmailTitle(), this.renderEmailText(), this.renderEmailDivider]
return ([
<Section full-width='full-width' padding-top="0">
{ content }
</Section>,
<Section>
{ innerContent }
</Section>
])
}
Well, render method of a component can only return one element. so you'll have to wrap it in a divas Zargold mentioned.
Note that MJML component are more than a standard React component.
It has some internal logic not available in a React context. IMO you should generate MJML as standard HTML element and render it with a renderToStaticMarkup then pass it to mjml2html function as a string and mjml will compiles
return (
<mjml>
<mj-body>
<mj-container>
... // your sections goes here
</mj-container>
</mj-body>
</mjml>
)
Note that I don't think React is the best suited for this kind of work, I would recommend you to use a templating language such as mustache/handlebars which fit better.
You cannot use JSX interspersed with JavaScript like that... you could either do (you must have only one parent/root element).
<div>
<Section full-width='full-width' padding-top="0">
{ content }
</Section>
<Section>
{ innerContent }
</Section>
</div>
Or You could if you insist on using an array for some reason:
renderSection(content, fullWidth){
return (
<Section
full-width={fullWidth ? 'full-width' : false}
style={{paddingTop: fullWidth ? 0 : 'auto'}}
>
{content}
</Section>
)
}
render(){
let contents = [content, innerContent]
return(
<div>
{contents.map(section, i => renderSection(section, i % 2 === 0))
</div>
)
Related
I've have started seeing the following notice in the web developer console when editing posts in Gutenberg:
wp.blockEditor.RichText multiline prop is deprecated since version 6.1 and will be removed in version 6.3. Please use nested blocks (InnerBlocks) instead.
I am unsure how I would go about converting my custom static Gutenberg block that currently uses <RichText> with the multiline property into a <div> with <InnerBlocks /> that still honor the original functionality that I built. This is a simplified example of what the edit() function currently looks like for the block:
edit: ( props ) => {
const blockProps = useBlockProps( { className: 'custom-cta p-0 mb-0' } );
const { attributes: { blurb }, setAttributes, className, isSelected } = props;
return (
<div { ...blockProps }>
<RichText
tagName="div"
multiline="p"
className="custom-cta__blurb w-100"
translate-name="blurb"
onChange={ value => setAttributes( { blurb: value } ) }
placeholder={ __( 'Subtext goes here (optional)', 'hello-tools' ) }
value={ blurb }
allowedFormats={ [ 'core/bold', 'core/italic' ] }
focusOnInsert={ false }
/>
</div>
);
}
This might come fairly close to what you are looking for. Try it - you might have to add some more attributes/settings and possibly CSS to get the exact result you are looking for.
This will not be able to replace your current block - so what I mean is, it is not backwards compatible, since it is a totally new block.
/**
* #see ./edit.js
*/
edit: () => {
// define which blocks to show when the block gets inserted
const TEMPLATE = [['core/paragraph']];
// define what blocks are allowed to be used
let allowedBlocks = ['core/paragraph'];
const blockProps = useBlockProps({className: 'my-custom-class'});
return (
<div {...blockProps}>
<div className={'custom-cta__blurb w-100'}>
<InnerBlocks
allowedBlocks={allowedBlocks}
template={TEMPLATE}
// false will let the user add and move the blocks
templateLock={false}
/>
</div>
</div>
)
}
```
I cant contribute to the discussion under the solution above, but I believe Frizzant is mistaken. Wordpress has included a solution in their list item block on GitHub, but I do not understand how to implement it.
How do I render before or after a child element in a container?
I am learning React by integrating it into my own website. I started with this:
function createErrorSection(name, title, description) {
const section = document.createElement('section');
const container = document.createElement('div');
const h2 = document.createElement('h2');
const p = document.createElement('p');
section.appendChild(container);
container.appendChild(h2);
container.appendChild(p);
section.id = name;
section.classList = 'section-error';
container.classList = 'section-error-container';
h2.textContent = title;
p.textContent = description;
return section;
}
Which I turned into this:
function createErrorSection(name, title, description) {
return (
<section id={name} className='section-error'>
<div className='section-error-container'>
<h2>{title}</h2>
<p>{description}</p>
</div>
</section>
);
}
This is eventually propagated down to either node.before(section) or node.after(section).
I checked inside ReactDOM, ReactDOM/server and React with no luck. I saw I could create an HTML string, but I need an HTMLElement and would rather not do my own rendering if it can be avoided (I want to learn the React way, I already know the vanilla way).
My end goal is to learn how and when to use React properly. I'd love to know the proper way, but insight, advice and workarounds are also greatly appreciated!
In React you rather want to create a custom component with a single argument which contains the corresponding properties:
// single argument contains all props
function ErrorSection({name, title, description}) {
return (
<section id={name} className='section-error'>
<div className='section-error-container'>
<h2>{title}</h2>
<p>{description}</p>
</div>
</section>
);
}
now you need to import ReactDOM and call render in order to show the component ErrorSecion with some specific property values inside a HTML node with the id #app. Make sure that your HTML document contains such a node.
import ReactDOM from "react-dom";
ReactDOM.render(
<ErrorSection name="..." title="..." description="..." />,
document.querySelector("#app")
);
Most of the react apps render some dynamically generated nested components into the DOM using a single empty HTML node inside the document body (e.g. div#app or div#root). So you most likely will only need to have a single ReactDOM.render call in your entire project.
First of all, component's name should be written in PascalCase.
In React, you should rethink the way you render elements.
There are different approaches for different purposes:
Pass components to the children prop
const Wrapper = ({ children }) => (
<div className="wrapper">
<h1>Wrapper heading</h1>
{children}
</div>
);
Now you can pass children to the wrapper this way:
const AnotherComponent = () => (
<Wrapper>
<div>Element that will be rendered where the children prop is placed</div>.
</Wrapper>
);
Pass components to custom props:
If you need to render many components in different spots, you can do this:
const MultiSpotComponent = ({ HeaderComponent, FooterComponent }) => (
<div>
{HeaderComponent}
<div>Some content</div>
{FooterComponent}
</div>
);
And then pass your components to the props the same way you do with attributes in HTML:
<MultiSpotComponent HeaderComponent={CustomHeader} FooterComponent={CustomFooter} />
Notice that I used self-closing tag for the component, because I don't render children inside it.
Render list
const AnotherComponent = () => {
const dynamicArray = ['some', 'dynamic', 'values'];
return (
<div>
{dynamicArray.map(value => <div key={value}>{value}</div>)}
</div>
);
};
I have described only 3 most-used approaches, but there are more ways to render elements. You can learn more at Official React Documentation
I'm building a website with React and for the "news" section i have a list of 3 components representing the different news. They belong to a flexbox and I'm trying to make them responsive. For mobile devices i would want only one of the 3 components to be shown with the ability of clicking 2 arrows to go through the news. Like a normal image carousel, but made of components. How can I achieve this? These are the components
The code where i put all the "news"
render() {
let content = <div>Loading...</div>;
if (!this.state.isLoading) {
content = (
<Aux>
<New
image={img1}
title={this.state.news[0].title}
text={this.state.news[0].text}
date={this.state.news[0].date}
classes="new-1"
/>
<New
image={img2}
title={this.state.news[1].title}
text={this.state.news[1].text}
date={this.state.news[1].date}
classes="new-2"
/>
<New
image={img3}
title={this.state.news[2].title}
text={this.state.news[2].text}
date={this.state.news[2].date}
/>
</Aux>
);
}
return content;
}
This is the "new" component
const New = props => {
const imageStyle = {
backgroundImage: `url(${props.image})`
};
return (
<div className={`new-wrapper ${props.classes}`}>
<div className="new-image" style={imageStyle}></div>
<div className="new-content">
<h4>{props.title}</h4>
<div className="text-wrapper">
<p>{props.text}</p>
</div>
<div className="date">
<span>{props.date}</span>
</div>
</div>
</div>
);
};
To achieve your desired result I would use a carousel plugin like https://react-slick.neostack.com/
You could set it to show three slides on desktop and just one on mobile so then you would get the arrows to go through the news cards.
I would also loop every element of the array by using the map function to render all the news. That way it would dynamically create a news card for every element on your state or array. See this example How to render an array of objects in React?
Hope this helps!
I have tried finding the answer to this on StackOverflow and there are some related posts (e.g. React Child Component Not Updating After Parent State Change) but I want to understand why this is not working...
I have a React application that will display a layout of character cards (that is, each card displays a different character). It uses a child component, CharacterBoard, that lays out the CharacterCards, which would be a grandchild component. I pass the characters down from the App to the CharacterBoard as props, and CharacterBoard in turn maps these out the CharacterCards.
The problem is that I want the state of the character to change when I click on one of them. Specifically, I want the revealed field to change. However, even though the state change is reflected in the array of characters in the App (that is, the revealed field changes correctly), and the change is reflected in the array of characters in CharacterBoard, but not in CharacterCard. In fact, my mapping does not seem to be called at all in CharacterBoard when the props change.
Do I need to use something like getDerivedStateFromProps in CharacterBoard and set the state of that component and then use the state to map the values down to CharacterCard? If so, why?
In short (tl;dr), can you pass props on down through the component chain and map them out along the way and still have all changes reflected automatically?
Thanks for any guidance.
If it helps, the render method of my App is
render() {
const {state: {characters}} = this
return (
<div>
<header>
</header>
<main>
<CharacterBoard
onCardSelected={this.onCardSelected}
rowSize={logic.ROW_SIZE}
characters={characters}
cardSize={this.CARD_SIZE}/>
</main>
</div>
);
}
that of CharacterBoard is
render() {
const {props: {characters, rowSize, cardSize,onCardSelected}} = this
const rowUnit = 12 / rowSize
const cardLayout = characters
.map((character, i) => (
<Col xs={6} sm={rowUnit} key={character.name}>
<CharacterCard
onCardSelected = {onCardSelected}
key={i + Math.random()}
character={character}
cardSize={cardSize}
/>
</Col>
)
)
return (
<div>
<Container>
<Row>
{cardLayout}
</Row>
</Container>
</div>
)
}
and finally CharacterCard has this render method
render() {
const {props: {character, cardSize}} = this
const {thumbnail, revealed} = character
const imgURL = `${thumbnail.path}/${cardSize}.${thumbnail.extension}`
const topCardClass = classNames('characterCard__card-back', {'characterCard__card-back--hidden': revealed})
console.log(revealed)
return < a href="/#" onClick={this.onCardSelected}>
<div className='characterCard__card'>
<div className={topCardClass}>
<img src="/images/card_back.png" alt=""/>
</div>
< div className='characterCard__card-front'>< img alt=''
src={imgURL}/>
</div>
</div>
</a>
}
Doh! A simple forgetting to setState in App. Knowing that it should work made me go back through the code one more time and see that, indeed, it was a stupid error on my part.
This question already has answers here:
ReactJS convert HTML string to JSX
(12 answers)
Closed 2 years ago.
I am building something with React where I need to insert HTML with React Variables in JSX. Is there a way to have a variable like so:
var thisIsMyCopy = '<p>copy copy copy <strong>strong copy</strong></p>';
and to insert it into react like so, and have it work?
render: function() {
return (
<div className="content">{thisIsMyCopy}</div>
);
}
and have it insert the HTML as expected? I haven't seen or heard anything about a react function that could do this inline, or a method of parsing things that would allow this to work.
You can use dangerouslySetInnerHTML, e.g.
render: function() {
return (
<div className="content" dangerouslySetInnerHTML={{__html: thisIsMyCopy}}></div>
);
}
Note that dangerouslySetInnerHTML can be dangerous if you do not know what is in the HTML string you are injecting. This is because malicious client side code can be injected via script tags.
It is probably a good idea to sanitize the HTML string via a utility such as DOMPurify if you are not 100% sure the HTML you are rendering is XSS (cross-site scripting) safe.
Example:
import DOMPurify from 'dompurify'
const thisIsMyCopy = '<p>copy copy copy <strong>strong copy</strong></p>';
render: function() {
return (
<div className="content" dangerouslySetInnerHTML={{__html: DOMPurify.sanitize(thisIsMyCopy)}}></div>
);
}
dangerouslySetInnerHTML has many disadvantage because it set inside the tag.
I suggest you to use some react wrapper like i found one here on npm for this purpose.
html-react-parser does the same job.
import Parser from 'html-react-parser';
var thisIsMyCopy = '<p>copy copy copy <strong>strong copy</strong></p>';
render: function() {
return (
<div className="content">{Parser(thisIsMyCopy)}</div>
);
}
Very Simple :)
UPDATE
in the latest version as usage explained:
// ES Modules
import parse from 'html-react-parser';
// CommonJS
const parse = require('html-react-parser');
....
//Parse single element
parse('<li>Item 1</li><li>Item 2</li>');
//Parse multiple elements
parse('<li>Item 1</li><li>Item 2</li>');
By using '' you are making it to a string. Use without inverted commas it will work fine.
const App = () => {
const span = <span> whatever your string </span>
const dynamicString = "Hehe";
const dynamicStringSpan = <span> {`${dynamicString}`} </span>
return (
<div>
{span}
{dynamicStringSpan}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
import { Fragment } from 'react' // react version > 16.0
var thisIsMyCopy = (
<Fragment>
<p>copy copy copy
<strong>strong copy</strong>
</p>
</Fragment>
)
By using '' the sets the value to a string and React has no way of knowing that it is a HTML element. You can do the following to let React know it is a HTML element -
Remove the '' and it would work
Use <Fragment> to return a HTML element.
To avoid linter errors, I use it like this:
render() {
const props = {
dangerouslySetInnerHTML: { __html: '<br/>' },
};
return (
<div {...props}></div>
);
}
You don't need any special library or "dangerous" attribute. You can just use React Refs to manipulate the DOM:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.divRef = React.createRef();
this.myHTML = "<p>Hello World!</p>"
}
componentDidMount() {
this.divRef.current.innerHTML = this.myHTML;
}
render() {
return (
<div ref={this.divRef}></div>
);
}
}
A working sample can be found here:
https://codepen.io/bemipefe/pen/mdEjaMK
Try Fragment, if you don't want any of above.
In your case, we can write
import React, {useState, Fragment} from 'react'
const thisIsMyCopy = Fragment('<p>copy copy copy <strong>strong copy</strong></p>')
render: function() {
return (
<div className="content">{thisIsMyCopy}</div>
);
}
If you using hook want to set it in a state somewhere with any condition
const [thisIsMyCopy, setThisIsMyCopy] = useState(<Fragment><p>copy copy copy <strong>strong copy</strong></p></Fragment>);
If anyone else still lands here. With ES6 you can create your html variable like so:
render(){
var thisIsMyCopy = (
<p>copy copy copy <strong>strong copy</strong></p>
);
return(
<div>
{thisIsMyCopy}
</div>
)
}
You can also include this HTML in ReactDOM like this:
var thisIsMyCopy = (<p>copy copy copy <strong>strong copy</strong></p>);
ReactDOM.render(<div className="content">{thisIsMyCopy}</div>, document.getElementById('app'));
Here are two links link and link2 from React documentation which could be helpful.