Render one component several times in different html pages with different text - javascript

I'm new to react and I'm trying to render one component in different html files (because I'm working in an existing project), each of them with different text.
I'm thinking of something like this:
class ctaSection extends React.Component{
render(){
return(
<div className="cta-section">
<div className="md:w-9c">
<h5 className="uppercase">{this.props.h5}</h5>
<h3>{this.props.h3}</h3>
</div>
<div className="cta-button">
<a href="#">
<button className="w-full">{this.props.button}</button>
</a>
</div>
</div>
);
}
}
export default ctaSection;
Then, in my index.js, I'm rendering like this, passing the props:
let ctaPage1 = document.getElementById('cta-section-page-1');
let ctaPage2 = document.getElementById('cta-section-page-2');
ReactDOM.render(<CtaSection h3='my text for page 1' h5='my h5 for page 1' button='hello'/>, ctaPage1);
ReactDOM.render(<CtaSection h3='text for page 2' h5='something' button='click me'/>, ctaPage2);
I'm not sure if this is the best and simpler way to do this, because I'm calling ReactDOM.render twice for same component, and I got this error:
Uncaught Invariant Violation: Target container is not a DOM element.
This works fine if I render the component once, but not for multiple instances.
What is the best way to do it?

I've thought about this more, and I'm not experienced with adding React to existing websites, but here's what I'd suggest:
I would have 1 JS file per HTML page that imports CtaSection, each of those files would look like such:
// page1.js
import CtaSection from '../CtaSection'; // or wherever it is
let ctaPage = document.getElementById('cta-section-page');
ReactDOM.render(<CtaSection h3='my text for page 1' h5='my h5 for page 1' button='hello'/>, ctaPage);
You would just need to make sure each html page uses the corresponding JS file.
// page1.html
<body>
<div id="cta-section-page"></div>
<script src="page1.js"></script>
</body>
Repeat for each page.

Related

ReactJS: Javascript not targetting div element in react but targets div element in html

I have a piece of javascript in the body section of index.html that modifies a div element with id='aww-wrapper'.
If I create a div element in the body of the index.html file above the script like so, once the webpage gets loaded, the script modifies the div element as intended.
<div style="width: 600px; height: 400px;" id="aww-wrapper"></div>
<div id="root"></div>
<script type="text/javascript">
var aww = new AwwBoard('#aww-wrapper', {
});
</script>
I need to do the same thing using react, however, if I make a react div element with the same ID in the App.js file instead of in the html file, the script no longer modifies/acts on the react div element.
import './App.css';
function App() {
return (
<div className="App">
<div style={{width:'600px', height:'400px'}} id="aww-wrapper">
</div>
</div>
);
}
export default App;
The code is meant to create a whiteboard app using the following awwapp.com code. I dont know whats wrong with the react implementation, is the script tag running first before the react div is made?
If you'd like to try this yourself, place this in the head section of index.html and use the html code above to create the whiteboard.
<head>
<script src="https://awwapp.com/static/widget/js/aww3.min.js"></script>
</head>
You cant mix React with an external script like that, because React handles the rendering.
The aww script cant find the element because React hasnt renderd it yet. So it would need to run after the render. But, if you do happen to get the element, React might just re-render it on some state change, which will cause your whiteboard to disappear again.
Unless they provide an npm package, which I cant seem to find, you cant load it the way youre trying to.

How can I add a script function to React

I am trying to add an external application Chameleon onto my react application and for that I have to add the javascript function to my application.
I only want it to be called in specific situations so I don't want to load it in my index.html. I tried adding it to the render function of my component as:
render() {
return(
<div>
<head>
<script type="text/javascript">/* Chameleon - better user onboarding */!function(t,n,o){var a="chmln",c="setup identify alias track clear set show on off custom help _data".split(" ");n[a]||(n[a]={}),n[a].accountToken=o,n[a].location=n.location.href.toString();for(var e=0;e<c.length;e++)!function(){var t=n[a][c[e]+"_a"]=[];n[a][c[e]]=function(){t.push(arguments)}}();var s=t.createElement("script");s.src="https://fast.trychameleon.com/messo/"+o+"/messo.min.js",s.async=!0,t.head.appendChild(s)}(document,window,"TOKEN");
chmln.identify(USER.ID_IN_DB, { // Unique ID of each user in your database (e.g. 23443 or "590b80e5f433ea81b96c9bf6")
email: USER.EMAIL });
</script>
...
...
</head>
</div>
)
}
But the above doesn't seem to work. I tried the same inside a helmet but no luck. Both of them show an error for
SyntaxError: Unexpected token
Is there a way I can load this function in a specific component or do I have to do it in the index.html?
You seem to have a strong misunderstanding of what react is for and how it is used.
1) There should only ever be 1 head element on the page, and it should be in index.html not in the rendered output of a component.
2) Having a component render a <script> tag goes against the point of using react.
What you need to do is import the code you need into your component:
import './path/to/file.js'
And then from there chmln should be available on the window object
window.chmln.identify()

Parse and Render external HTML in React component

I'm writing a React-based application where one of the components receives its HTML content as a string field in props. This content is returned by an API call.
I need to:
Render this content as a standard HTML (i.e. with the styles applied)
Parse the content to see if the sections within the content have "accept-comments" tag and show a "Comment" button beside the section
For example, if I receive the HTML below, I should show the "Comment" button beside section with id "s101".
<html>
<head/>
<body>
<div id="content">
<section id="s101" accept-comments="true">Some text that needs comments</section>
<section id="s102">Some text that doesn't need comments</section>
</div>
</body>
</html>
Questions:
What would be the most efficient way to parse and render the HTML as the content can get a bit large, close to 1MB at times?
How can I ensure that React does not re-render this component as it will not be updated? I'd assume always return "false" from shouldComponentUpdate().
Things I've tried:
Render the HTML with "dangerouslySetInnerHTML" or "react-html-parser". With this option, cannot parse the "accept-comments" sections.
Use DOMParser().parseFromString to parse the content. How do I render its output in a React component as HTML? Will this be efficient with 1MB+ content?
This answer comes from Chris G's code in the comments. I used the code with different sizes of documents and it works well. Thanks Chris G!
Posting the code here in case the link link in the comments breaks.
The solution uses DOMParser to parse the HTML content provided by the API call and scans it to find the content that should include the "Comment" button. Here are the relevant parts.
import React from "react";
import { render } from "react-dom";
const HTML =
"<div><section but='yes'>Section 1</section><section>Section 2</section></div>";
class DOMTest extends React.Component {
constructor(props) {
super(props);
const doc = new DOMParser().parseFromString(HTML, "application/xml");
const htmlSections = doc.childNodes[0].childNodes;
this.sections = Object.keys(htmlSections).map((key, i) => {
let el = htmlSections[key];
let contents = [<p>{el.innerHTML}</p>];
if (el.hasAttribute("but")) contents.push(<button>Comment</button>);
return <div key={i}>{contents}</div>;
});
}
render() {
return <div>{this.sections}</div>;
}
}
const App = () => (
<div>
<DOMTest />
</div>
);
render(<App />, document.getElementById("root"));

React: How to use same component twice in same page but loading one script tag for both just one time

I created a react component that I want to use twice(or more) inside my page, and I need to load a script tag for it inside the head of my page but just once! I mean even if I use the component twice or more in the page it should add the script tag just once in the head.
The Problem is that this script tag should be absolutely a part of the component and not statically inserted in the head of my page.
Can anyone help me to make the magic happens? Thanks a lot in advance!
You can give react-helmet a try for managing changes to your <head> from within React components.
In particular, you can check this example where rendering the same element four times only adds the script tag once.
For completeness, the relevant code from the example (although the interesting part is to see how it executes):
import { Helmet } from "react-helmet";
function ComponentWithHeader() {
return (
<div>
<div>Oh hi</div>
<Helmet>
<script src="fake-url.js" />
</Helmet>
</div>
);
}
const App = () => (
<div>
<ComponentWithHeader />
<ComponentWithHeader />
<ComponentWithHeader />
<ComponentWithHeader />
</div>
);
You can set the state of the parent component to keep in memory that the script is already added.
if (!this.state.scriptAdded) {
// Add script tag
this.setState({ scriptAdded: true });
}

How to insert and nest a component within another in ractive.js when the components are in two separate files?

I've been learning ractive.js for few weeks and I am trying to build a feature within a larger webpage that lists out three items. Each list has it's own title and description. Each list is a component (the list-component) with its own styles and javascript (each component is a separate html). I also have a component (description-component) that is responsible for writing out the title and description and it is in a separate html file. The problem I am having is 'importing' or bringing in that description-component into the list-component so the output in the main index html file is this:
<description title="Name">
<list-items/>
</description>
So far I looked into the yield directive and tried some examples but that was a simple example using two components on the same document. I am not sure if that is the correct way. This is what is in the description-component html file:
<p>{{title}}</p>
I tried using ractive-load.js and load up the description-component html for every list-component file like in this example on their github:
Ractive.load({
Foo: 'my-components/foo.html',
Bar: 'my-components/bar.html'
}).then( function ( components ) {
var foo = new components.Foo({
el: 'body',
data: { ... }
});
var bar = new components.Bar({
el: 'body',
data: { ... }
});
}).catch( handleError );
and that seemed like an overkill and thought there must be a better way. How would I go about approaching this?
The loading of Ractive components is not directly related to handling nested components and passing yielded content to components.
Ractive.load works by fetching one or more html files that it then resolves to Components. From your code samples, it seems you've got that part working. For larger apps, I usually use a build tool plugin to pre-bundle all my Ractive components so they're deliverable in one file or even rolled into my main bundle js.
However the components are made available, they need to be registered on either the consuming component (or a view parent) or globally:
Ractive.load({
Foo: 'my-components/foo.html',
Bar: 'my-components/bar.html'
}).then( function ( components ) {
Ractive.components.foo = components.Foo;
Ractive.components.bar = components.Bar;
// now create your actual top-level view instance(s)
}).catch( handleError );
In component architectures, you create trees or bushes of components. I usually only have one top level app component, but it certainly is feasible to create multiple trees that start at different places in the DOM.
For simplicity, continuing on the above example, let's create a generic ractive instance that uses the two components Foo and Bar we registered (notice we use the property name we assigned to Ractive.components):
const ractive = new Ractive({
el: document.body,
sayHello() {
alert('hello from main Ractive instance');
},
template: `
<h1>my kewl app</h1>
<foo>
<h3 on-click="sayHello()">hello world</h3>
<bar bizz="{{buzz}}"></bar>
</foo>
`
});
In this case we're passing some content (html and our bar component) to the foo component by including it as the <foo> element content.
How this content is used depends on the foo template. There are two choices:
<div>
<h2>foo component template</h2>
{{>content}}
<p>some more stuff</p>
</div>
In this example, we're using the built-in partial "content" to tell the template to put provided content in the {{>content}} slot. In this case the provided content is passed like a partial, and any directives will be applied against the foo component. So in this example, clicking on the h3 header will try and run foo.sayHello(). And when passing the bizz data to the bar component, Ractive will start looking in the foo component for buzz.
Often, this isn't what you want. What you would rather have happen is for the parent to own the directives. So instead the foo template would look like:
<div>
<h2>foo component template</h2>
{{yield}}
<p>some more stuff</p>
</div>
And now when h3 is clicked, it calls the main ractive.sayHello() method as the content was passed to be rendered in the DOM by the the foo component, but it was still owned by the passing instance. Likewise Ractive will start look for buzz in the main instance, not foo.
With yield you can also name multiple partials to be passed:
<!-- "foo" template: -->
<div>
<header>{{yield header}}</header>
<section>
<div>something here</div>
<div>{{yield message}}</div>
</section>
</div>
<!-- using "foo": -->
<div>
<foo>
{{#partial header}}
<h2>This is the header to use</h2>
{{/partial}}
{{#partial message}}
<p>This is the message to use, with a bar component to boot</p>
<bar></bar>
{{/partial}}
</foo>
</div>

Categories

Resources