I'm using Gatsby v4.25.3 and I'm trying convert the loading of some third party scripts to the new <Script> tag. I've converted the load of three Bootstrap related files in my html.js file:
import { Script } from "gatsby";
import PropTypes from "prop-types"
import React from "react"
export default function HTML(props) {
return (
<html {...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"
/>
{props.headComponents}
</head>
<body {...props.bodyAttributes}>
{props.preBodyComponents}
<noscript key="noscript" id="gatsby-noscript">
This app works best with JavaScript enabled.
</noscript>
<div
key={`body`}
id="___gatsby"
dangerouslySetInnerHTML={{ __html: props.body }}
/>
{props.postBodyComponents}
<Script onLoad={() => console.log("success")}
onError={() => console.log("sadness")} src="/bootstrap.min.js" />
<Script onLoad={() => console.log("success")}
onError={() => console.log("sadness")} src="/jquery.min.js" />
<Script onLoad={() => console.log("success")}
onError={() => console.log("sadness")} src="/popper.min.js" />
<div id="recent-comments" style={{ display: 'none' }}>
<style dangerouslySetInnerHTML={{
__html:
`.form-preview {
display: flex;
flex-direction: column;
justify-content: center;
}`}} />
</body>
</html>
)
}
HTML.propTypes = {
htmlAttributes: PropTypes.object,
headComponents: PropTypes.array,
bodyAttributes: PropTypes.object,
preBodyComponents: PropTypes.array,
body: PropTypes.string,
postBodyComponents: PropTypes.array,
}
Unfortunately, I don't get any errors/warnings/log entries in the browser console for any of the files. Looking in the network dev tab I also don't see the files being requested. The files are served correctly if I manually request them.
I've looked at the documentation and don't see what I'm missing:
https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-script/
Have you tried setting them in any other file/component rather than the html.js?
const Layout = ({ children }) => {
return (
<>
<Header/>
<Script onLoad={() => console.log("success")}
onError={() => console.log("sadness")} src="/bootstrap.min.js" />
<Script onLoad={() => console.log("success")}
onError={() => console.log("sadness")} src="/jquery.min.js" />
<Script onLoad={() => console.log("success")}
onError={() => console.log("sadness")} src="/popper.min.js" />
{children}
<Footer/>
</>
)
};
export default Layout;
To me, it looks that the strategy of adding it in the html.js collides with the postBodyComponents (or any other onRenderBody parameters) and with the rehydration of the custom html.js
Related
I've have been going through the code in this really fascinating project https://github.com/MaximeHeckel/linear-vaporwave-react-three-fiber. It's a 3D next.js app that allows 3D rendering and animating of meshes and camera views in the browser. I would love to convert this code to tsx instead of the original js. How would I do that...?
This is an example of a random boilerplate page written in tsx:
import type { NextPage } from 'next'
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
const Home: NextPage = () => {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1 className={styles.title}>
Welcome to Next.js!
</h1>
<p className={styles.description}>
Get started by editing{' '}
<code className={styles.code}>pages/index.tsx</code>
</p>
<div className={styles.grid}>
<a href="https://nextjs.org/docs" className={styles.card}>
<h2>Documentation →</h2>
<p>Find in-depth information about Next.js features and API.</p>
</a>
<a href="https://nextjs.org/learn" className={styles.card}>
<h2>Learn →</h2>
<p>Learn about Next.js in an interactive course with quizzes!</p>
</a>
<a
href="https://github.com/vercel/next.js/tree/canary/examples"
className={styles.card}
>
<h2>Examples →</h2>
<p>Discover and deploy boilerplate example Next.js projects.</p>
</a>
<a
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}
>
<h2>Deploy →</h2>
<p>
Instantly deploy your Next.js site to a public URL with Vercel.
</p>
</a>
</div>
</main>
<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a>
</footer>
</div>
)
}
export default Home
This is the type of code that I'm used to working with in Next.js
This is a little snippet from pages/index.js in https://github.com/MaximeHeckel/linear-vaporwave-react-three-fiber
const Scene = () => {
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
return (
<>
{!mounted ? null : (
<Canvas
style={{
position: "absolute",
display: "block",
top: 0,
left: 0,
zIndex: -1,
outline: "none",
}}
dpr={Math.min(window.devicePixelRatio, 2)}
linear
antialias
>
<React.Suspense fallback={null}>
<color attach="background" args={["#000000"]} />
<fog attach="fog" args={["#000000", 1, 2.5]} />
<OrbitControls attach="orbitControls" />
<PerspectiveCamera
makeDefault
position={[0, 0.06, 1.1]}
fov={75}
near={0.01}
far={20}
/>
<Light />
<Landscape />
<Effects />
</React.Suspense>
</Canvas>
)}
</>
);
};
export default function Home() {
return (
<div>
<Head>
<title>Linear - React-Three-Fiber</title>
<meta
name="description"
content="A reversed-engineer versioned of the WebGL animation from the Linear 2021 release page. Recreated by #MaximeHeckel"
/>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<div className="label-container">
<p className="label">
⚡️ Originally inspired by the{" "}
<a href="https://linear.app/releases/2021-06">
2021 Linear release page
</a>
</p>
<p className="label">
✨ Reverse-engineered and recreated by{" "}
#MaximeHeckel with
React-Three-Fiber
</p>
<p className="label">
👉 How I built this?{" "}
<a href="https://blog.maximeheckel.com/posts/vaporwave-3d-scene-with-threejs/">
Building a Vaporwave scene with Three.js
</a>{" "}
(Three.js only)
</p>
</div>
<Scene />
</main>
</div>
);
}
I figure "Home" is easy to convert because I can just change export default function Home() { to const Home: NextPage = () => { and add an export default Home at the bottom of the page.
"Scene" looks more challenging to convert. First of all the return html is wrapped in <> and </> which is unfamiliar to me, and I keep seeing things like React.useEffect and React.Suspense. It seems weird to declare "React" first instead of just "useEffect."
Could someone please convert the "Scene" const to typescript code and from there I will be able to translate the rest of the file?
'Const scene' should work in TS aswell, you can add FC and an interface though, if you want to be able to use props later!
The empty '<>' is just used to wrap your return. If you want to return multiple HTML like this
<div></div>
<div></div>
You will have to wrap them in a parent tag. Like this:
<div>
<div></div>
<div></div>
</div>
You could switch out the empty the empty '<>' to something else aswell, like a div!
For the 'React.useState' etc, you can just import them from react and use them directly!
import {useEffect, useState, FC, Suspense} from 'react'
interface Props {}
const Scene:FC<Props> = (props) => {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
return (
<>
{!mounted ? null : (
<Canvas
style={{
position: "absolute",
display: "block",
top: 0,
left: 0,
zIndex: -1,
outline: "none",
}}
dpr={Math.min(window.devicePixelRatio, 2)}
linear
antialias
>
<Suspense fallback={null}>
<color attach="background" args={["#000000"]} />
<fog attach="fog" args={["#000000", 1, 2.5]} />
<OrbitControls attach="orbitControls" />
<PerspectiveCamera
makeDefault
position={[0, 0.06, 1.1]}
fov={75}
near={0.01}
far={20}
/>
<Light />
<Landscape />
<Effects />
</Suspense>
</Canvas>
)}
</>
);
};
I'm trying to render the list of words using MUI pagination but in alphabetical order.
what we have
1,2,3,...,26
what we need
A,B,C,...,Z
wordsList: {
A: [],
B: [],
...,
Z: []
}
You can do it like this:
renderItem={(item) => (
<PaginationItem {...item} page={alphabet[item.page - 1]} />
)}
const { Pagination, PaginationItem } = MaterialUI;
const alphabet = Array.from("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
function BasicPagination() {
return (
<Pagination
count={alphabet.length}
variant="outlined"
renderItem={(item) => (
<PaginationItem {...item} page={alphabet[item.page - 1]} />
)}
/>
);
}
ReactDOM.createRoot(document.querySelector("#root")).render(<BasicPagination />);
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<script crossorigin src="https://unpkg.com/#mui/material#5/umd/material-ui.development.js"></script>
<div id="root"></div>
I am trying to fetch coingecko-api to access live price of bitcoin. I am trying to pass return props of getServerSideProps to my <CalculatorBuy /> component which is the part of <Main /> component. I was trying to import async function in calculatorbuy.js but i'm getting undefined object. How to pass props from index.js to main.js and calculatorbuy.js components. In index.js everything work like a charm but i would like to use props value directly in components.
index.js
export default function Home(props) {
const {data} = props.result;
console.log(data);
return (
<div className="container">
<Head>
<title>Buy BTC</title>
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"></meta>
</Head>
<Header />
<Main />
<Footer />
<style jsx> {`
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
`} </style>
</div>
)
}
export async function getServerSideProps(context) {
const result = await coinGeckoClient.simple.price({
ids: "bitcoin",
vs_currencies: "eur",
});
return {
props: {
result,
},
}
}
main.js
import React, { useState } from 'react';
import Button from '#material-ui/core/Button';
import Calculatorbuy from './calculatorbuy.js'
import Calculatorsell from './calculatorsell.js'
export default function Main() {
const [ showMe, setShowMe ] = useState(true);
function toggle (){
if (!showMe) {
setShowMe(true);
}
else {
setShowMe(true);
}
}
function toggle2 (){
if (showMe) {
setShowMe(false);
}
else {
setShowMe(false);
}
}
return (
<main className="main">
<div className="box">
<div className="buttons">
<Button onClick={toggle} variant="outlined" color="primary" style={{width: 120, marginRight: 10}}>
KUP
</Button>
<Button onClick={toggle2} variant="outlined" color="secondary" style={{width: 120, marginRight: 10}}>
SPRZEDAJ
</Button>
<Button variant="outlined" color="default" style={{width: 120,}}>
HISTORIA
</Button>
</div>
<div style={{ display: showMe?"block":"none" }}>
<Calculatorbuy />
</div>
<div style={{ display: !showMe?"block":"none" }}>
<Calculatorsell />
</div>
</div>
<div className="room-for-socials"></div>
import React, { useState } from 'react';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import Livebsv from './livebsv.js';
export default function Calculatorbuy() {
const [value, setValue] = useState(0);
return (
<form className="calculator" noValidate autoComplete="off">
<div>
<Livebsv />
</div>
<div className="typebox">
<div className="textfield">
<TextField error={false} id="outlined-number" label="PLN" helperText="Min. wartość 100zł"
type="tel"
value={value}
InputProps={{
inputProps: { min: "100", max: "5000", step: "0.01" }
}}
variant="outlined"
onKeyPress={(e) => {
if (!/[0-9]/.test(e.key)) {
e.preventDefault();
}
}}
onChange={(e) => setValue(e.currentTarget.value)}
onKeyPress={(e) => {
if (!/[0-9]/.test(e.key)) {
e.preventDefault();
}
}}
onBlur={(e) => {
if (e.currentTarget.value > 0 & e.currentTarget.value < 100 )
setValue(100);
else if (e.currentTarget.value > 5000)
setValue(5000);
}}
/>
</div>
<div className="textfield">
<TextField disabled id="outlined-disabled" value={(value).toFixed(8)} label="BSV" variant="outlined"
You started well by loading the result on index.js(getServerSideProps).
Then, to pass the data to Main, you have to add it as a property for the component:
<Main data={data} />
Now, as Main expects a parameter, it has to be defined in main.js:
export default function Main(props) {
const data = props.data;
...
}
Then, for the Calculatorbuy component you have to do the same like on Main. Define the props and use it.
GatsbyJS beginner here, trying to get the Bulma responsive menu toggle to apply the "is-active" class to the menu (gatsby v2 with gatsby starter netlify cms). all code here: https://github.com/pddew/gatsby-starter-netlify-cms
Currently the toggle button and script tags appear but the button doesn't respond.
There is a working version of this in the gatsby starter business- when I inspect and compare my site with this, I can't spot the error, only that there is no event listener on toggle button, when it seems there should be.
When I inspect the site, the toggle.js script is being called and put in before the closing body tag, and the viewed.
I have tried building and deploying with no luck, clearing caches and swapping the scripts for bulma's suggested code.
Here is the relevant code.
Any help with this greatly appreciated; I'm a bit stuck!
in Layout.js:
import React from 'react' import Helmet from 'react-helmet'
import Navbar from '../components/Navbar' import Footer from '../components/Footer' import './all.sass'
const TemplateWrapper = ({ children }) => ( <div>
<Helmet title="Immediate Start Jobs" />
<Navbar />
<div>{children}</div>
<Footer /> </div> )
export default TemplateWrapper
in Navbar:
<button className="button navbar-burger" data-target="navMenu">
<span />
<span />
<span />
</button>
</div>
<div className="navbar-menu" id="navMenu">
<div className="navbar-start">
<Link className="navbar-item" to="/about">
About
</Link>
<Link className="navbar-item" to="/products">
Products
</Link>
<Link className="navbar-item" to="/blog">
Blog
</Link>
</div>
in html.js
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}
<script src={__PATH_PREFIX__ + '/js/toggle.js'} />
</body>
</html>
)
}
}
HTML.propTypes = {
htmlAttributes: PropTypes.object,
headComponents: PropTypes.array,
bodyAttributes: PropTypes.object,
preBodyComponents: PropTypes.array,
body: PropTypes.string,
postBodyComponents: PropTypes.array,
}
And my toggle.js, stored in static/js/
document.addEventListener('DOMContentLoaded', function () {
// Get all "navbar-burger" elements
var $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0)
// Check if there are any navbar burgers
if ($navbarBurgers.length > 0) {
// Add a click event on each of them
$navbarBurgers.forEach(function ($el) {
$el.addEventListener('click', function () {
// Get the target from the "data-target" attribute
var target = $el.dataset.target
var $target = document.getElementById(target)
// Toggle the className on both the "navbar-burger" and the "navbar-menu"
$el.classList.toggle('is-active')
$target.classList.toggle('is-active')
})
})
}
})
I'm new to Gatsby myself, but I had to find a way to get my navbar to function. My solution is pretty quick and dirty, but it works. If your building a website with a lot of components that change state, I would suggest using redux and a central store instead. My solution was just having a navbar that handled its own state.
import React, { Component } from 'react'
import Link from 'gatsby-link'
class Navbar extends Component {
state = {
//This sets the state of Bulma elements
navbarIsActive: "navbar-item has-dropdown"
}
//This opens the navbar dropdown
navbarOpenDropdown = () => {
this.setState({
navbarIsActive: "navbar-item has-dropdown is-active"
})
}
//This closes the navbar dropdown
navbarCloseDropdown = () => {
this.setState({
navbarIsActive: "navbar-item has-dropdown"
})
}
render() {
return(
<div>
<nav class="navbar is-transparent" role="navigation" aria-label="dropdown navigation">
<a class="navbar-item">
<h1>Title!</h1>
</a>
<div
class={this.state.navbarIsActive}
onMouseEnter={this.navbarOpenDropdown}
onMouseLeave={this.navbarCloseDropdown}
>
<a class="navbar-link">
Docs
</a>
<div class="navbar-dropdown is-boxed">
<Link to="/">Home</Link>
<Link to="/about">About Us</Link>
<Link to="/blog">Blog</Link>
<hr class="navbar-divider"/>
<div class="navbar-item">
Version 0.7.2
</div>
</div>
</div>
</nav>
<section class="hero">
<div class="hero-body">
<p class="title">
Documentation
</p>
<p class="subtitle">
Everything you need to <strong>create a website</strong> with Bulma
</p>
</div>
</section>
</div>
)
}
}
export default Navbar;
That's just my two pennies' worth: https://nhpcr.codesandbox.io/
src/Navbar.js
import React from 'react';
import PropTypes from 'prop-types';
const NavbarItem = props => (
<a className="navbar-item is-capitalized" href={`#${props.page}`}>
{props.page}
</a>
);
const NavbarBurger = props => (
<button
onClick={props.toggleMenu}
className={`button navbar-burger ${props.active ? 'is-active' : ''}`}
>
<span />
<span />
<span />
</button>
);
export default class Navbar extends React.Component {
state = {
activeMenu: false,
};
toggleMenu = () => {
this.setState({
activeMenu: !this.state.activeMenu,
});
};
render() {
let { pages = [], color } = this.props;
let navbarItems = pages.map(page => <NavbarItem page={page} key={page} />);
return (
<nav className={`navbar is-fixed-top is-${color}`}>
<div className="navbar-brand">
<NavbarItem page="logo" />
<NavbarBurger
active={this.state.activeMenu}
toggleMenu={this.toggleMenu}
/>
</div>
<div
className={`navbar-menu ${this.state.activeMenu ? 'is-active' : ''}`}
>
<div className="navbar-start">{navbarItems}</div>
</div>
</nav>
);
}
}
Navbar.propTypes = {
pages: PropTypes.array.isRequired,
color: PropTypes.string,
};
src/index.js
import React from 'react';
import { render } from 'react-dom';
import Navbar from './Navbar';
import 'bulma/css/bulma.css';
const styles = {
fontFamily: 'sans-serif',
textAlign: 'center',
};
const pages = ['about', 'contact', 'sitemap'];
const App = () => (
<div style={styles}>
<Navbar pages={pages} />
</div>
);
render(<App />, document.getElementById('root'));
I have this simple app, where I'm looking for a solution to connect the click from the text to the image. Here, the click is not toggeable, just click once and the image is supposed to appear. First, I thought that would make sense to define a state for the image, but honestly I don't think is the best practice.
Which is the best solution for this kind of situation? I aprecciate any provided tips and solutions.
Thank you.
// IMAGE COMPONENT
class Image extends React.Component {
render() {
return (
<div>
<img
alt=""
src="https://media.makeameme.org/created/what-if-I-y0ivox.jpg"
/>
</div>
);
}
}
// TEXT COMPONENT
class Text extends React.Component {
imageClick() {
console.log('click');
}
render() {
return (
<div>
<p onClick={this.imageClick}>If you click me, Morpheus will appear!</p>
</div>
)
}
}
// PARENT COMPONENT
class App extends React.Component {
render () {
return (
<div>
<Text onClick={this.imageClick} />
<Image src={this.props.src} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
Use the parent component to set the visibility of the image.
// IMAGE COMPONENT
class Image extends React.Component {
render() {
return (
<div>
<img
alt=""
src="https://media.makeameme.org/created/what-if-I-y0ivox.jpg"
/>
</div>
);
}
}
// TEXT COMPONENT
class Text extends React.Component {
render() {
return (
<div>
<p onClick={() => this.props.onClick()}>If you click me, Morpheus will appear!</p>
</div>
)
}
}
// PARENT COMPONENT
class App extends React.Component {
state = {
visible : false,
}
toggleVisible = () => this.setState({ visible : !this.state.visible });
render () {
const { visible } = this.state;
return (
<div>
<Text onClick={this.toggleVisible} />
{
visible
&& <Image src={this.props.src} />
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("#app"));