Gatsby jquery slick $ is not defined - javascript

I have a problem with gatsby 5, it imports jquery and slick.js to it using "Helmet" without refreshing the page, the script works but as soon as I click f5 (clear the cache), I suddenly get an error that "$ is not defined" or that "jquery s not defined"I feel that it may be a fault that they will not load in the correct js order, but I might be wrong. Will you help?
export function Head() {
return (
<>
<title>Simtopia</title>
<Helmet>
<script
src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
crossorigin="anonymous"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js"
integrity="sha512-XtmMtDEcNz2j7ekrtHvOVR4iwwaD6o/FUJe6+Zq+HgcCsk3kj4uSQQR8weQ2QVj1o0Pk6PwYLohm206ZzNfubg=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<script
src="/js/localJS.js"
></script>
</Helmet>
</>
)
}

If you are loading scripts that are mutually dependent I'd recommend using the Script API that Gatsby provides, which exposes an onLoad callback:
import React, { useState } from "react";
import { Script } from "gatsby";
export function Head() {
const [loaded, setLoaded] = useState(false);
return (
<>
<Script
src="https://code.jquery.com/jquery-3.6.0.min.js"
onLoad={() => setLoaded(true)}
/>
{loaded && (
<Script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js" />
)}
{loaded && (
<Script src="/js/localJS.js" />
)}
</>
);
}
Note: tweak the order or the conditions accordingly
⚠️
Outside the scope of the question: don't import jQuery inside React apps, or you will break the hydration. Really, don't do it.
React (so Gatsby) creates and manipulates a virtual DOM (vDOM) while jQuery manipulates directly the real DOM. Using both will cause unwanted (re)hydration issues because when something changes in the DOM React won't be aware and vice-versa. This translates into multiple issues, but one of the most typical is finding unstyled parts of the page, non-rendered hooks when moving forward/backward using browser's arrows/history, etc.
From React docs:
React is unaware of changes made to the DOM outside of React. It
determines updates based on its own internal representation, and if
the same DOM nodes are manipulated by another library, React gets
confused and has no way to recover.
Alternatively, use React-based approaches such Reack Slick or using useRef hook when pointing DOM elements

Related

How to get another component on ReactJs use ES6 modules

i need help for integrate frontend ReactJS on my site. currently i avoid to use nodeJS or NPM for implement my reactJS. but i'm using ReactJS CDN. so i not run npm start when develop the reactJS. i just want to use the frontend.
Hope this will understand from the start. so i have a question, how do i able to get another component and place it on single file.
I Created file App.js. I plan that this file will become a centralize for other components.
let me share what i'm doing right now.
here is my file structure
this is my code on index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Buynow Project</title>
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script type="text/babel" src="buynow/index.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
this is my code on index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js'
ReactDOM.render(<App />, document.getElementById('root'));
this is my code on App.js
import React from 'react';
import Navbar from './components/Navbar.js'
function App(props){
return (
<div>
<Navbar/>
Hello App
</div>
);
}
export default App;
this is my code on Navbar.js
import React from 'react';
function NavBar(props) {
return (
<div>
<Navbar/>
Hi NavBar
</div>
);
}
export default NavBar;
but i got this error, and pointed on file index.js line:1
Uncaught ReferenceError: require is not defined
this is error
I confuse how to solve this. I start with the simple code just for testing if it can work or not.
please help.
https://reactjs.org/docs/react-without-jsx.html
JSX is not a requirement for using React. Using React without JSX is
especially convenient when you don’t want to set up compilation in
your build environment.
So you can't use JSX/TSX directly in your browser (need compilation). Also, you can't use import outside a module (so basically now, you can already use your function components by adding the import of all your files in the index.html).
In my experience I suggest that you use the benefits of JSX / TSX and compile your code with NPM to be more comfortable (everything is way more intuitive with jsx).
In any case, in the link that I have included, you will find how to do the equivalent of JSX with pure javascript. That's what you need. (as you can see in my small snippet)
If you want keep using react from CDN and without compiling, your code, should be something like this:
// For the Snippet i have all of your js here
// But you could just import in your index.html every separate component file you have (without any import/export syntax)
function NavBar(props) {
return React.createElement('span', null,props.title);
}
function App(props){
return React.createElement('span', null, React.createElement(NavBar, {title: 'Header'}, null),
React.createElement('span', null,'Hello App'));
}
ReactDOM.render(React.createElement(App, {}, null), document.getElementById('root'));
<html>
<head>
<meta charset="UTF-8" />
<title>Buynow Project</title>
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
Anyway, as you see, the code is not so readable this way...
EDIT
I made the same snippet on stackblitz with separated files
https://stackblitz.com/edit/web-platform-4ruju9?file=index.html
Ps. Also, don't put NavBar inside NavBar or you'll get a render loop
You have to enable module support in script tag
<script type="text/babel" data-type="module" src="./index.js"></script>
Remove React import statements as they can be directly used.

Prevent page flash in Next.js 12 with Tailwind CSS class-based dark mode

How can one prevent the page flash when using class-based dark mode in Tailwind CSS with Next.js v12 without using any 3rd party pkgs like next-themes?
I've looked at:
this Q&A How to fix dark mode background color flicker in NextJS? and while it's a valid/working solution in Next.js <v12, in v12 it no longer works and throws warnings in dev which suprisingly turn to a build blocking error in prod env
Do not add <script> tags using next/head (see inline <script>). Use next/script instead. See more info here: https://nextjs.org/docs/messages/no-script-tags-in-head-component
This article https://www.vidyasource.com/blog/dark-mode-nextjs-tailwindcss-react-hooks however including the script <Script strategy="beforeInteractive" src="/scripts/darkMode.js"/> still results in a page flash as it adds defer to it in the head
This official Tailwind CSS dark mode doc on what's required https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
I think they restrict putting things in Head from v12 to prepare for Suspense / Streaming / React v18.
Either way, I'm lost on how to do it without next-themes, does anyone know how can we just inject that bit of script to prevent that page flash?
Hope this question makes sense, if not, please give me a shout.
I like simple and minimalistic things, hence the aim to reduce the dependency on 3rd party pkgs, such a simple thing should be possible without overcomplicated solution IMO.
I had the same problem and solved it like this in Next 12.1.0:
Create theme.js within the public folder (mine has the following content):
;(function initTheme() {
var theme = localStorage.getItem('theme') || 'light'
if (theme === 'dark') {
document.querySelector('html').classList.add('dark')
}
})()
Add <Script src="/theme.js" strategy="beforeInteractive" /> to _app.tsx or _app.jsx. This is important - I tried putting it inside _document.tsx which didn't work. The final _app.tsx looks like this:
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import Head from 'next/head'
import Script from 'next/script'
function App({ Component, pageProps }: AppProps) {
return <>
<Head>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
<Script src="/theme.js" strategy="beforeInteractive" />
<Component {...pageProps} />
</>
}
export default App
Works like a charm for me - no flickering and all Next.js magic. :) Hope this works for you too.

How to add custom local JS files in Gatsby correctly

I´m a newbie on react and gatsby but i´m working on a little project as practice anda I have a little problem. I want to add a custom JS file to the project (little functions for a calculator on the index). I used Helmet to import them and on develop enviroment is working fine, but once build, is not.
import Helmet from "react-helmet"
import { withPrefix, Link } from "gatsby"
export default function homePage() {
return (
<main>
<Helmet>
<script src={withPrefix('/functions.js')} type="text/javascript" />
<script src={withPrefix('/escritura.js')} type="text/javascript" />
</Helmet>
}
I´m not sure what I am doing wrong. Someone can help me, please? You can see the project proof version live here:
https://modest-hoover-aac2d1.netlify.app/
In the final version, every input should be filled automatically, but is not happening.
withPrefix is a helper function that only works in build mode because in development mode paths don't need to be prefixed:
For pathnames you construct manually, there’s a helper function,
withPrefix that prepends your path prefix in production (but doesn’t
during development where paths don’t need to be prefixed).
So if your code works well under development mode, just remove withPrefix and leave your code as:
export default function homePage() {
return (
<main>
<Helmet>
<script src={`/functions.js`} type="text/javascript" />
<script src={`/escritura.js`} type="text/javascript" />
</Helmet>
}

How to import external html file to React app?

I have a project in a middle of development. And I need to use external landing page as a home page. Therefore, I need to import landings index.html, but it has its own folders with css and js(mainly Jquery code).
I wanted to import it as <iframe src={html}></iframe> into my project but my app doesn't seem to load htmls.
What are best ways to import html files that use own jquery code to react?
This is a bit tricky, and there might be other ways (perhaps better) to achieve the same result. Also, I would consider the performance impact of loading multiple libraries into an existing React app.
With that humble disclaimer out of the way, one way to do this would be to include jQuery directly into React's main index.html page using <script> tags, this will make $ globally available across the app:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</body>
</html>
Once this is done, place the landing page project folder (along with its dependencies) inside the public directory:
Then, from the main app component load the desired landing page using fetch, then use .text() to transform the retrieved page into regular text.
Use setState to set the retrieved HTML into the app state, then inside render() use a regular <div> container to store the landing page and use the React attribute dangerouslySetInnerHTML to set HTML inside that target container.
Finally, I pass an anonymous function (as a second parameter to setState) and use jQuery's getScript() to load and execute the required JS libraries that the landing page depends on.
In the example, I loaded Bootstrap's JS, which is needed to power the Carousel.
Bootstrap's CSS is loaded directly from the landing page's HTML file using a standard <link> tag.
import React from "react";
class App extends React.Component {
state = {
page: null
};
componentDidMount() {
fetch("landing-page-one/index.html")
.then(result => {
return result.text();
})
.then(page => {
this.setState(
{
page: { __html: page }
},
() => {
window.$.getScript("landing-page-one/js/index.js");
}
);
});
}
render() {
const { page } = this.state;
return (
<>
<h2>
<span>Inserting project using React's </span>
<code>dangerouslySetInnerHTML</code>:
</h2>
<div
dangerouslySetInnerHTML={page && page}
/>
</>
);
}
}
export default App;
Working example here.
I'm just going to through this out there as a spitball... I've never tried it, but why not directly replace index.html and not render anything on that page? You would have to either adjust the build somehow... I have no idea how to, or manually put it after the react app finishes building. I'm sure it would be a bug nightmare, but it honestly sounds a little less of a hastle than an iframe which might give you some weird UI behavior. React-Server, even though it says server, really just takes stringified JSX and I think html translates it back to workable code. You could also try that approach, but this does sound like a nightmare project.
I was also trying to find out a solution and figured out two ways for different use cases:
If your HTML file contains <div> and other child tags (that can be nested under other <div> or <body> tags) then this approach will work for you:
First you need to configure webpack to be able to parse HTML files.
You can either do that with npm eject or use another module
react-app-rewired - and then add a HTML loader in overrides.
Then import the HTML file and use a parser. I believe it's better than using dangerouslySetInnerHTML.
Example with Method 1:
const parse = require("html-react-parser");
const docs = require("../../public/redoc-static.html").default;
const ApiDocs = (props: any) => {
return (
<Box sx={{ display: "flex", height: "100vh" }}>
<Box>{parse(docs)}</Box>
</Box>
);
};
If your HTML file contains a <html> tag (is a complete file) following the above approach would give the very obvious error <html> cannot appear as a child of <div>. In that case, the workaround I found is to open the whole HTML file in a different tab of the browser with _blank:
Example with Method 2:
window.open(filename, "_blank")
Note: The HTML file has to reside in the public folder of your project.

How to include local javascript on a Gatsby page?

I'm a total React newbie and I guess there is something fundamental I don't quite understand here. A default Gatsby page looks like this. Is there a way to use a local .js file somewhat like this?
<script src="../script/script.js"></script>
What I would like to achieve is to have react ignore script.js but still have the client side use it. A default Gatsby page looks like this, is it possible to do somerthing like that there?
import React from "react"
import { Link } from "gatsby"
import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"
const IndexPage = () => (
<Layout>
<SEO title="Home" keywords={[`gatsby`, `application`, `react`]} />
<h1>Hi people</h1>
<p>Welcome to your new Gatsby site.</p>
<p>Now go build something great.</p>
<div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
<Image />
</div>
<Link to="/page-2/">Go to page 2</Link>
</Layout>
)
After several hours of frustration I finally stumbled upon discussion on GitHub that solved this for me. In Gatsby, there is a thing called static folder, for which one use case is including a small script outside of the bundled code.
Anyone else in the same situation, try proceeding as follows:
Create a folder static to the root of your project.
Put your script script.js in the folder static.
Include the script in your react dom with react-helmet.
So in the case of the code I posted in my original question, for instance:
import React from "react"
import Helmet from "react-helmet"
import { withPrefix, Link } from "gatsby"
import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"
const IndexPage = () => (
<Layout>
<Helmet>
<script src={withPrefix('script.js')} type="text/javascript" />
</Helmet>
<SEO title="Home" keywords={[`gatsby`, `application`, `react`]} />
<h1>Hi people</h1>
<p>Welcome to your new Gatsby site.</p>
<p>Now go build something great.</p>
<div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
<Image />
</div>
<Link to="/page-2/">Go to page 2</Link>
</Layout>
)
Notice the imports
import Helmet from "react-helmet"
import { withPrefix, Link } from "gatsby"
and the script element.
<Helmet>
<script src={withPrefix('script.js')} type="text/javascript" />
</Helmet>
This would have saved hours of my time, hopefully this does it for someone else.
There are many ways to add scripts in GatsbyJS...
To execute a script on a specific page
create a stateless ScriptComponent.js file and place it inside your /src folder.
in your ScriptComponent.js use require() to execute the script inside useEffect() like this:
const ScriptComponent = ({
src, // if internal,put a path relative to this component
onScriptLoad = () => {}, // cb
appendToHead = false,
timeoutDuration = 10,
defer = false,
isExternal = false,
}) => {
useEffect(() => {
setTimeout(() => {
if (isExternal) {
const script = document.createElement('script');
script.src = src;
script.onload = onScriptLoad;
defer
? script.defer = true
: script.async = true;
appendToHead
? document.head.appendChild(script)
: document.body.appendChild(script);
} else { // for internal scripts
// This runs the script
const myScript = require(src);
}
}, timeoutDuration);
}, []);
return null;
};
To run it on client-side, you could check the window object inside your script.js file if you didn't run it in useEffect:
if(typeof window !== 'undefined' && window.document) {
// Your script here...
}
finally, go to the page you want to execute the script in it (e.g. /pages/myPage.js ), and add the component <ScriptComponent />
If you want to execute a script globally in (every component/page) you could use the html.js file.
first, you'll have to extract the file (in case you didn't) by running:
cp .cache/default-html.js src/html.js
inside your html.js file:
<script dangerouslySetInnerHTML= {{ __html:`
// your script here...
// or you could also reuse the same approach as in useEffect above
`}} />
Just create gatsby-ssr.js file on root folder
and add the following pattern for your scripts folder
import React from 'react'
export const onRenderBody = ({ setPostBodyComponents }) => {
setPostBodyComponents([
<script
key="https://code.jquery.com/jquery-3.2.1.slim.min.js"
src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossOrigin="anonymous"
defer
/>,
<script
key="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossOrigin="anonymous"
defer
/>,
<script
key="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossOrigin="anonymous"
defer
/>
])
}
Then, you at the end of dom you'll see the links to scripts
If you'd like to use a Gatsby plugin, which to me is no different from using an external library like Helmet (plugins are npm packages after all), you could use gatsby-plugin-load-script.
You can provide either the url to the src attribute or a local path. If you're going to store your JS in a local file such as some-minified-js.min.js - make sure to store in the static directory at the root of your project.
Once you do this, you can access via the global object:
global.<object or func name here>
For example, I was trying to include a very small JS library via a minified file, so I stored the file in /static/my-minified-library.min.js and then:
Installed the plugin: npm i --save gatsby-plugin-load-script
Added this to my gatsby-config.js
plugins: [
{
resolve: "gatsby-plugin-load-script",
options: {
src: "/my-minified-library.min.js",
},
},
],
Accessed in my react component like so:
useEffect(() => {
const x = new global.MyImportedLibraryObject();
}, []}
Gatsby uses html.js in the src folder. Not index.html like most react projects.
Example html.js file:
import React from "react"
import PropTypes from "prop-types"
export default class HTML extends React.Component {
render() {
return (
<html {...this.props.htmlAttributes}>
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
{this.props.headComponents}
</head>
<body {...this.props.bodyAttributes}>
{this.props.preBodyComponents}
<div
key={`body`}
id="___gatsby"
dangerouslySetInnerHTML={{ __html: this.props.body }}
/>
{this.props.postBodyComponents}
</body>
</html>
)
}
}
HTML.propTypes = {
htmlAttributes: PropTypes.object,
headComponents: PropTypes.array,
bodyAttributes: PropTypes.object,
preBodyComponents: PropTypes.array,
body: PropTypes.string,
postBodyComponents: PropTypes.array,
}
For adding custom Javascript using dangerouslySetInnerHTML inside src/html.js:
<script
dangerouslySetInnerHTML={{
__html: `
var name = 'world';
console.log('Hello ' + name);
`,
}}
/>
You can try adding your js there but, note that your js may not work as expected. You can always look into react-helmet for more dynamic apps and adding scripts to <head>.
Gatsby Documentation: https://www.gatsbyjs.org/docs/custom-html/
You can do this very easily with the Gatsby plugin "gatsby-plugin-load-script."
Simply do this:
Create a folder named static at the root of your gatsby app
Place your script in it
Add the following configuration in gatsby-config.js
{
resolve: 'gatsby-plugin-load-script',
options: {
src: '/test-script.js', // Change to the script filename
},
},
I'm not sure if anyone still needs this answer, but here it goes:
The answer by Elliot Marques is excellent. If you need it for a local file, upload the script to Github and use a service like JSDelivr. It saves a lot of time and stress.
React works with dynamic DOM. But for rendering it by browser, your web server should send a static index page, where React will be included as another script tag.
So, take a look on your index.html page, which you can find in public folder. There you could insert your script tag in the header section, for example.

Categories

Resources