Integrate a Spreadshop site using React - javascript

I am building a website using React, and I want to integrate a shop from spreadshop.com / spreadshirt.com, to my React website.
Spreadshop.com gives quite good instructions on how to use script-tags to integrate their shop to an html-site (instructions can be found here).
I am not an experienced programmer, and I'm a little confused about how scripts work with React and especially about where I should put the spread_shop_config, which the Spreadshop instructions tells me to have available in window Scope. I have tried to use a useScript-hook, which I found here. But it doesn't seem to work.
Below is my implementation attempt so far.
What am I doing wrong? How can I integrate the Spreadshirt shop to my React website?
import React from 'react';
import useScript from './Hooks/useScript';
function Shop() {
const [loaded, error] = useScript('https://shop.spreadshirt.no/shopfiles/shopclient/shopclient.nocache.js');
var spread_shop_config = {
shopName: 'awesome-merch-shop',
locale: 'US',
prefix: 'https://shop.spreadshirt.com',
baseId: 'shop'
}
return (
<div className="shopBody">
{loaded && !error && (
<div id="shop">
<div>
<i id="spinner" className="fa fa-spinner" aria-hidden="true"/>
<p>Loading The Shop...</p>
</div>
</div>
)}
</div>
);
}
export default Shop;

I'll answer myself here, since I figured a better way and which actually works (better) for all scripts even if you need to add multiple different scripts to a page/component.
(Now I'm actually confused why we need a useScript-hook at all).
Inside the react component that needs to use the script, it seems better to use javascript to create a script-element and then append it to the document.body.
So in example, to add the Spreadshop.com script inside the Shop-component:
import React from 'react';
import './Shop.css';
function Shop() {
var js = document.createElement("script");
js.type = "text/javascript";
js.src = "https://shop.spreadshirt.no/shopfiles/shopclient/shopclient.nocache.js";
document.body.appendChild(js);
return (
<div className="shopBody">
<div id="shop">
<i id="spinner" className="fa fa-spinner" aria-hidden="true"/>
<p>Loading The Shop...</p>
</div>
</div>
);
}
export default Shop;
And the Spreadshop configurations variable I just add normally with script tags inside the index.html in the publics folder.
<script>
var spread_shop_config = {
shopName: 'awesome-merch-shop',
locale: 'US',
prefix: 'https://shop.spreadshirt.com',
baseId: 'shop'
}
</script>
By doing it this way, the Spreadshop script won't give an error message when loading every component, because the script is only appended when that component loads.
And also, since the script is loaded whenever that component is loaded, it will work even if you refresh the page, while if the script was only in the index.html then the index.html would have to be loaded for the script to be loaded.

Include the javascript and Spreadshirt config in your high-level index.js file. For most it's something like /public/index.html (the same folder you have your manifest.json, robots.txt, etc) along with my google fonts and bootstrap include before </html>
For my implementation I added
<script>
var spread_shop_config = {
shopName: 'YourShopNameHere',
locale: 'us_US',
prefix: 'https://shop.spreadshirt.com',
baseId: 'myShop'
};
</script>
<script type="text/javascript" src="https://shop.spreadshirt.com/shopfiles/shopclient/shopclient.nocache.js"></script>
And then in the React component:
import React from 'react';
import './Shop.css';
type Props = {
testProp: string
};
export const Shop = ({ testProp }: Props) => {
return (
<div id="Shop-Component">
<div id="myShop">
<div>is loading text here...</div>
</div>
</div>
);
};
export default(Shop);

Related

Next.js script render

In my Next.js project I have a script that renders a widget as follows:
<a
className="e-widget no-button xdga generic-loader"
href="https://example"
rel="nofollow"
>
3 lucky winners stand a chance to win…
</a>
<Script
type="text/javascript"
src="https://widget.gleamjs.io/e.js"
strategy="afterInteractive"
></Script>
The problem is that once this script executes on the initial render, it does not execute again later.
Anyone know how I can get the script to execute multiple times?
The issue might be originating from other parts but you can give this a shot:
import React, { FC } from "react";
import Script from "next/script";
interface Props {
id: string;
}
const Widget: FC<Props> = ({ id }) => {
return (
<>
<a
className="e-widget no-button xdga generic-loader"
href="https://example"
rel="nofollow"
>
3 lucky winners stand a chance to win…
</a>
<Script
id={id}
src="https://widget.gleamjs.io/e.js"
strategy="afterInteractive"
/>
</>
);
};
export default Widget;
I managed to solve the problem by forcing the parent component to re-mount, which then causes my Script tag to re execute/render as desired.

Vanilla JavaScript to React Class/Component

I am new to react and this is my first site and I have some plan JavaScript I found online that will allow a word to be typed out and updated over a course of time. I have already made it into a react Component. But I am not sure how to covert this JavaScript Function into react code.
Here is my new React Component.
import React, {Component} from 'react';
class Hero extends Component {
render () {
return (
<div>
<section id="hero" className="d-flex flex-column justify-content-center align-items-center">
<div className="hero-container" data-aos="fade-in">
<h1>Augusto J. Rodriguez</h1>
<p>I'm a <span className="typed" data-typed-items="opption1, opption2, opption3, opption4"></span></p>
</div>
</section>
</div>
);
}
}
export default Hero;
Here is the vanilla JavaScript that I want to use in my code. Currently this is living in my main.js file that is being called from the index.html. this is the only part of the code that is not working.
if ($('.typed').length) {
var typed_strings = $('.typed').data('typed-items');
typed_strings = typed_strings.split(',')
new Typed('.typed', {
strings: typed_strings,
loop: true,
typeSpeed: 100,
backSpeed: 50,
backDelay: 2000
});
}
I am assuming I need to create a function where my tag is. But I am not sure how to do that in React.
any article references would be awesome or tips on how to resolve this. I have the full code for this project on GitHub.
Looks like that piece of code is using a library called Typed.js.
From looking at your project, I see you setup the Typed.js library inside your public/assets/vendor folder. Instead I would recommend using the NPM package manager to install and setup Typed.js, and copy over the code to work the React way. https://github.com/mattboldt/typed.js/.
Here's an example using Typed.js with React. https://jsfiddle.net/mattboldt/ovat9jmp/
class TypedReactDemo extends React.Component {
componentDidMount() {
// If you want to pass more options as props, simply add
// your desired props to this destructuring assignment.
const { strings } = this.props;
// You can pass other options here, such as typing speed, back speed, etc.
const options = {
strings: strings,
typeSpeed: 50,
backSpeed: 50
};
// this.el refers to the <span> in the render() method
this.typed = new Typed(this.el, options);
}
componentWillUnmount() {
// Make sure to destroy Typed instance on unmounting
// to prevent memory leaks
this.typed.destroy();
}
render() {
return (
<div className="wrap">
<h1>Typed.js</h1>
<div className="type-wrap">
<span
style={{ whiteSpace: 'pre' }}
ref={(el) => { this.el = el; }}
/>
</div>
<button onClick={() => this.typed.toggle()}>Toggle</button>
<button onClick={() => this.typed.start()}>Start</button>
<button onClick={() => this.typed.stop()}>Stop</button>
<button onClick={() => this.typed.reset()}>Reset</button>
<button onClick={() => this.typed.destroy()}>Destroy</button>
</div>
);
}
}
ReactDOM.render(
<TypedReactDemo
strings={[
'Some <i>strings</i> are slanted',
'Some <strong>strings</strong> are bold',
'HTML characters × ©'
]}
/>,
document.getElementById('react-root')
);
It looks like currently you're using the 'typed' library to create this typed list. There are some community packages that act as React wrappers for that, like this one:
https://www.npmjs.com/package/react-typed
Alternatively, you could do what that library does yourself, by loading the 'typed' package in a call to componentDidMount, passing in a React ref instead of a DOM element.
By the way, currently your code uses jQuery (assigned to the variable $) so it's not quite vanilla JS. You could replace the calls to $ with calls to document.querySelector, to make this vanilla JS (though your code might depend on jQuery elsewhere)

How can I embed VaniilaJS into React?

I have open source library that I want to use. the library wrote in clean vanilla js:
follow their docs, if I want to use the library:
<html>
<head>
<script src="./jquery-2.0.3.min.js"></script>
<script src="./kinetic-v5.1.0.min.js"></script>
<script src="./inchlib-1.2.0.js"></script>
<script>
$(document).ready(function() { //run when the whole page is loaded
var inchlib = new InCHlib({"target": "inchlib",
"width": 800,
"height": 1200,
"column_metadata_colors": "RdLrBu",
"heatmap_colors": "RdBkGr",
"max_percentile": 90,
"middle_percentile": 60,
"min_percentile": 10,
"heatmap_font_color": "white",
text: 'biojs'});
inchlib.read_data_from_file("/microarrays.json");
inchlib.draw();
inchlib.onAll(function(name){
console.log(name + " event triggered");
});
});
</script>
</head>
<body>
<div class="heatmaps" style="margin:auto; align-items: center; margin-left:25%;">
<div id="inchlib"></div>
</div>
<div ></div>
</body>
</html>
The file inchlib-1.2.0.js contains the main logic and js code. I want to build react project and use this library there. How can I achieve this goal?
import React, { Component } from 'react';
import './App.css';
export default class App extends Component {
render () {
return (
<div>
<div>
</div>
</div>
)
}
}
You can create custom hook with useEffect. In useEffect you should paste your code. You can insert html elements, add event listeners and so on.
useLibrary.js
import { useEffect } from "react";
const useLibrary = () => {
useEffect(() => {
$.getScript("inchlib-1.2.0.js", function(){
var inchlib = new InCHlib({"target": "inchlib",
"width": 800,
"height": 1200,
"column_metadata_colors": "RdLrBu",
"heatmap_colors": "RdBkGr",
"max_percentile": 90,
"middle_percentile": 60,
"min_percentile": 10,
"heatmap_font_color": "white",
text: 'biojs'});
inchlib.read_data_from_file("/microarrays.json");
inchlib.draw();
inchlib.onAll(function(name){
console.log(name + " event triggered");
});
});
}, []);
};
export default useLibrary;
App.js
import useLibrary from ".useLibrary";
export default class App extends Component {
useLibrary();
render () {
return (
<div>
<div class="heatmaps" style="margin:auto; align-items: center; margin-left:25%;">
<div id="inchlib"></div>
</div>
</div>
)
}
}
But I warn you that this is a big crutch.
Depends on what you're gonna do with the library you want to integrate with. Checkout this as a base reference: Integrating with other libraries.
If you're gonna manipulate DOM elements you'll gonna need a reference to them. In this case checkout this: Refs and the DOM.
If the library provides some general logic, you have no problem using it anywhere throughout your code or more specifically in effects.
As inchlib is a visual element library, you'll need to go the first route and get a reference to a specific DOM element. As already noted, checkout Refs from react docs.
Alternative solution is to wrap the whole library usage in your own react component.
Well If I were to do the same thing then I would paste the script tags as you've done in your html file
<head>
<script src="./jquery-2.0.3.min.js"></script>
<script src="./kinetic-v5.1.0.min.js"></script>
<script src="./inchlib-1.2.0.js"></script>
<script>
</head>
For accessing an object into react app, Create a file named Inchlib.js in same directory as is your app.js
Contents of Inchlib.js should be
export default window.InCHlib;
Import the default export into your app.js
import InCHlib from "./inchlib";
function App() {
console.log(InCHlib); // prints the InCHlib object
return "hello";
}
Note: Although this should work, there might be a better way to do this. Also using global objects in react code is not usually a preferred option.
Hopefully this would help.
Just add the Libraries and Scripts you want in the public/index.html file in your react project.
create loadScript function:
function loadScript(src, position, id) {
if (!position) {
return;
}
const script = document.createElement('script');
script.setAttribute('async', '');
script.setAttribute('id', id);
script.src = src;
position.appendChild(script);
}
in Component:
export default function GoogleMaps() {
const loaded = React.useRef(false);
if (typeof window !== 'undefined' && !loaded.current) {
if (!document.querySelector('#google-maps')) {
loadScript(
'https://maps.googleapis.com/maps/api/js?key=AIzaSyBwRp1e12ec1vOTtGiA4fcCt2sCUS78UYc&libraries=places',
document.querySelector('head'),
'google-maps',
);
}
loaded.current = true;
}
}
now you can access window.google
here is a example

Return Vue Component in a custom rendering function for a contentful embedded entry

I'm playing with Contentful! and I'm having trouble with Rich text content field.
I'm using '#contentful/rich-text-types' and #contentful/rich-text-html-renderer modules to customize the way this block is rendered and to display some assets and reference linked in Rich text content.
After calling getEntries in nuxt asyncData function, I've a description data available in my page component.
I'm using documentToHtmlString function with options.
Everything is working fine, but I would like to use a component I have already written (Post.vue), instead of returning the template in ES6 Template Strings.
I know that is possible, but I'm quite new to JS world.
I've tried to require components/post/Post.vue, but I don't know how to use it.
import { BLOCKS } from '#contentful/rich-text-types';
import { documentToHtmlString } from "#contentful/rich-text-html-renderer"
Vue component template where rich text field is rendered
<section class="container">
<div class="columns">
<div class="column">
<div v-html="formatContent(description)" />
</div>
</div>
</section>
I simply call formatContent method to call documentToHtmlString as follow (it works):
methods: {
formatContent(content) {
return documentToHtmlString(content, options)
}
}
And customize documentToHtmlString with options as described in doc:
const embeddedEntryRender = (node) => {
const { data: { target: entry} } = node
const fields = entry.fields
const sys = entry.sys
// LOOK HERE
// const postComponent = require('~/components/post/Post')
return `
<div class="column is-4">
<div class="card">
<div class="card-content">
<div class="media">
<div class="media-content">
<h3 class="title is-4">${fields.title}</h3>
<div class="subtitle is-6">${fields.description}</div>
</div>
</div>
<div class="content">
</div>
</div>
</div>
</div> `
}
const options = {
renderNode: {
[BLOCKS.EMBEDDED_ENTRY]: (node) => embeddedEntryRender(node),
// [BLOCKS.EMBEDDED_ASSET]: (node) => `<custom-component>${customComponentRenderer(node)}</custom-component>`
}
}
No errors detected
--
Thanks a lot
yep you can have a custom vue component in there with a different npm library, I had this same problem.
npm i contentful-rich-text-vue-renderer
in template:
<rich-text-renderer :document="document" :nodeRenderers="renderNode" />
where 'document' is the data sent form contentful, looks like your calling it description. RenderNode is a method described below.
in script:
data () {
return {
renderNode: [INLINES.ASSET_HYPERLINK]: (node, key, h) => {
return h('my-vue-component', { key: hey, props: { myProp: 'blah blah' }},'what I want inside the <my-vue-component> tag'`)
}
}
this might be kind of confusing. So First imprt the richTextRenderer component from that npm library and make sure to declare it in the components section of your vue component. (or gloablly)
Next pass into its 'document' prop the contentful rich text field
if you want custom rendering, pass into the nodeRenders prop a function (I had to declare it in the data section)
My example takes any asset hyperlink type and replaces it with a component of what I want inside the tag
I only got this to work if I globally declared the my-vue-component in the main.js file.
import MyVueComponent from 'wherever/it/is';
Vue.component('my-vue-component', MyVueComponent);
there are more configurations for this, just read the npm libs documentation (though its not great docs, it took my a long time to figure out how to pass props down, I had to read their github code to figure that out lol)

Cannot load data.json file using jQuery and React.js

I must say first that I come from mobile development. I am very new with web development so this is probably an amateur questions.
I am building a react.js project using create-react-app (which uses Babel). I am trying to follow a tutorial where I am supposed to import a *.json file when my component mounts. I can't seem to be able to access my data, and I get an error saying "filteredApts.map is not a function".
Here are my imports:
import React from 'react';
import './App.css';
import $ from 'jquery';
Here is my componentWillMount method:
componentDidMount: function() {
console.log($.get('./data.json', function(){}.bind(this))) //This seems to get an HTML Page returned!
this.serverRequest = $.get('./data.json', function(result) {
var tempApts = result;
this.setState({
myAppointments: tempApts
}); //setState
}.bind(this));
},
my render method:
render: function() {
var filteredApts = this.state.myAppointments;
filteredApts = filteredApts.map(function(item, index) {
return (<li className="pet-item media" key={index}>
<div className="pet-info media-body">
<div className="pet-head">
<span className="pet-name">{this.state.myAppointments[index].petName}</span>
<span className="apt-date pull right">{this.state.myAppointments[index].petName}</span>
</div>
<div className="owner-name"><span className="label-item">Owner:</span>
{this.state.myAppointments[index].ownerName}</div>
<div className="apt-notes">
{this.state.myAppointments[index].aptNotes}</div>
</div>
</li>)
}.bind(this));
return (<div className="interface">
<div className="item-list media-list">
<ul className="item-list media-list"></ul>
</div>
</div>)
} //render
}); //MainInterface
Here is my map of files:
And finally my console log:
Is somebody capable of pointing out what I might be doing wrong?
I'd suggest to change this line:
var filteredApts = this.state.myAppointments;
to this
var filteredApts = this.state.myAppointments || [];
You use asynchronous data fetching and at the moment of rendering it can be no myAppointments yet.
Looks like your responseText is html.
I figured it out.
First, './data.json' was pointing to the public folder by default.
Second, I was missing {filteredApts} in
return (<div className="interface">
<ul className="item-list media-list">{filteredApts}</ul>
</div>)
Thanks anyways to all.

Categories