ReactJS - Uncaught RangeError: Maximum call stack size exceeded - reactjs

I am trying to make a contact-manager-app from a YouTube video:
https://www.youtube.com/watch?v=0riHps91AzE&lc=Ugybk5M3ofjHsO8uHjd4AaABAg.9WHwkOL6qXV9WJu89p6VTV
Every time, I enter the inputs and click Add, the following error pops-up:
the screen-shot of the main page
I also get "6 moderate severity vulnerabilities" while downloading uuidv4. ( Put just in case, if it might help )
Also got "Module not found: Error: Can't resolve 'util' in 'C:\Users\loki\OneDrive\Desktop\ReactJS-YouTube\contact-app\node_modules\uuidv4\build\lib"
Here are all my files:
index.html
<!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="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" integrity="sha512-8bHTC73gkZ7rZ7vpqUQThUDhqcNFyYi2xgDgPDHc+GXVGHXq+xPjynxIopALmOPqzo9JZj0k6OqqewdGO3EsrQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
App.js
import React, { useState, useEffect } from "react";
import { uuid } from "uuidv4";
import "./App.css";
import Header from "./Header";
import AddContact from "./AddContact";
import ContactList from "./ContactList";
function App() {
const LOCAL_STORAGE_KEY = "contacts";
const [contacts, setContacts] = useState([]);
const addContactHandler = (contact) => {
console.log(contact);
setContacts([...contacts, { id: uuid(), ...contact }]);
};
const removeContactHandler = (id) => {
const newContactList = contacts.filter((contact) => {
return contact.id !== id;
});
setContacts(newContactList);
};
useEffect(() => {
const retriveContacts = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY));
if (retriveContacts) setContacts(retriveContacts);
}, []);
useEffect(() => {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(contacts));
}, [contacts]);
return (
<div className="ui container">
<Header />
<AddContact addContactHandler={addContactHandler} />
<ContactList contacts={contacts} getContactId={removeContactHandler} />
</div>
);
}
export default App;
ContactList.js
import React from "react";
import ContactCard from "./ContactCard";
const ContactList = (props) => {
console.log(props);
const deleteContactHandler = (id) => {
props.getContactId(id);
};
const renderContactList = props.contacts.map((contact) => {
return(
<ContactCard contact={contact} clickHandler = { deleteContactHandler } key = { contact.id}/>
);
})
return(
<div className="ui celled list">
{renderContactList}
</div>
);
}
export default ContactList;
ContactCard.js
import React from "react";
import user from "../images/user.jpg";
const CardContact = (props) => {
const {id, name, email} = props.contact;
return(
<div className="item">
<img className="ui avatar image" src={user} alt="user" />
<div className="content">
<div className="header">{name}</div>
<div>{email}</div>
</div>
<i className="trash alternate outline icon"
style={{color:"red",marginTop:"7px"}}
onClick={() => props.clickHandler(id)}>
</i>
</div>
);
};
export default CardContact;
AddContact.js
import React from "react";
class AddContact extends React.Component {
state = {
name: "",
email: "",
};
add = (e) => {
e.preventDefault();
if (this.state.name === "" || this.state.email === "") {
alert("ALl the fields are mandatory!");
return;
}
this.props.addContactHandler(this.state);
this.setState({ name: "", email: "" });
};
render() {
return (
<div className="ui main">
<h2>Add Contact</h2>
<form className="ui form" onSubmit={this.add}>
<div className="field">
<label>Name</label>
<input
type="text"
name="name"
placeholder="Name"
value={this.state.name}
onChange={(e) => this.setState({ name: e.target.value })}
/>
</div>
<div className="field">
<label>Email</label>
<input
type="text"
name="email"
placeholder="Email"
value={this.state.email}
onChange={(e) => this.setState({ email: e.target.value })}
/>
</div>
<button className="ui button blue">Add</button>
</form>
</div>
);
}
}
export default AddContact;

From the uuidv4 npm page:
Most of the functionality of uuidv4 module is already included in uuid since version 8.3.0, so most of the functions of uuidv4 module have already been marked as deprecated.
So, importing uuidv4 module in your App.js is causing this error.
You can upgrade to the latest version of uuid library to get rid of this error.
Run these commands in the terminal in your project directory.
npm uninstall uuidv4
npm install uuid
And now, in App.js import uuid module instead of uuidv4
import { v4 as uuid } from 'uuid';
And now, you can use uuid() function to create UUIDs
To check more about uuid, you can see there documentation: https://github.com/uuidjs/uuid#quickstart

In your App.js:
On each useEffect (page renders) you are calling setContract , on useEffect below you are watching that contract as dependency , so it's rendering when contract is changing
useEffect(() => {
const retriveContacts = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY));
if (retriveContacts) setContacts(retriveContacts);
}, []);
useEffect(() => {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(contacts));
}, [contacts]);

Related

Next.Js - Uncaught (in promise) Error: Cancel rendering route

I am trying to create basic authentication and route protection for pages in Next.Js, before I start connecting them to the backend (Express.Js).
However I am getting this error:
Uncaught (in promise) Error: Cancel rendering route
I have 2 layouts, Main Layout, which renders the PageHeader, SideBar and the content of the page, and Login layout, which only renders PageHeader and Login Form.
It tells me that the problem is in useUserSession.tsx, but I have no idea what is the error.
My useUserSession.tsx looks like this:
import { useRouter } from "next/router"
import { useCallback, useState } from "react"
const useUserSession = () => {
const[username, setUsername] = useState<any>()
const[userLoggedIn, setUserLoggedIn] = useState<any>()
const router = useRouter();
const login = useCallback(() => {
setUsername('Admin');
setUserLoggedIn(true);
router.push("/")
}, [])
const loggout = useCallback(() => {
setUsername('');
setUserLoggedIn(false);
router.push("/login")
}, [])
return {username, userLoggedIn, login, loggout }
}
export default useUserSession
And my userSession looks like this:
import React, { createContext } from 'react'
const userSession = createContext({
isLoggedIn: false,
username: String,
login: () => {
},
loggout: () => {
}
})
export default userSession;
My _app.tsx
import { useContext, useEffect } from 'react';
import { useRouter } from 'next/router';
export default function App({ Component, pageProps }: AppPropsWithLayout) {
const getLayout = Component.getLayout ?? ((page) => page)
const { username, userLoggedIn ,login, loggout } = useUserSession();
return getLayout(
<userSession.Provider value={{isLoggedIn: userLoggedIn, username, login: login, loggout}} >
<Component {...pageProps} />
</userSession.Provider>
)
}
My MainLayout.tsx
function MainLayout({children} : {children: ReactNode}) {
const { username, userLoggedIn ,login, loggout } = useUserSession();
return (
<userSession.Provider value={{isLoggedIn: userLoggedIn, username, login: login, loggout}} >
<PageHeader/>
<UserSideBar/>
<div className="pageContent">
{children}
</div>
</userSession.Provider >
)
}
export default MainLayout
My LoginLayout.tsx
function LoginRegisterLayout({children} : {children: ReactNode}) {
const { username, userLoggedIn ,login, loggout } = useUserSession();
return (
<userSession.Provider value={{isLoggedIn: userLoggedIn, username, login: login, loggout}} >
<PageHeader/>
{children}
</userSession.Provider>
)
}
export default LoginRegisterLayout
And this is my Login.tsx
const Login: PageWithLayout = () =>{
const errorMessagesClass = `${styles.disabled}`;
const userSessionData = useContext(userSession);
return (
<div className={styles.LoginPage}>
<div className={styles.LoginForm}>
<h1>Login</h1>
<form>
<div className={styles.LoginFormInputs}>
<div>
<label htmlFor='usernameInput'>Username or Email: </label>
<input type={'text'} id='usernameInput'></input>
</div>
<div>
<label htmlFor='passwordInput'>Password: </label>
<input type={'password'} id='passwordInput'/>
</div>
</div>
<div className={`${styles.ErrorMessages} ${errorMessagesClass}`}>
</div>
<button type='submit' >Login in to your account</button>
<Link href="#" onClick={userSessionData.login}>login</Link>
</form>
<p className={styles.FormFooter}>Don&apos;t have a profile ?
<Link href='/register'>Create one now
<i className="fa-solid fa-user-plus"></i>
</Link>
</p>
</div>
</div>
)
}
Login.getLayout = function getLayout(page: ReactElement){
return(
<LoginRegisterLayout>
{page}
</LoginRegisterLayout>
)
}
export default Login
And this is my Index.tsx that the user can visit only when he has logged in.
const Home: PageWithLayout = () => {
const userSessionData = useContext(userSession);
const router = useRouter();
useEffect(() => {
if(!userSessionData.isLoggedIn){
router.push("/login");
}
}, [])
return (
<>
<Head>
<title>Kiwi | Home page</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div className={styles.NewPostContainer} id="NewPostContainer">
<div className={styles.NewPostFormContainer} id="textAreaContainer">
<Image src='/images/user_icon.png' width="512" height="512" alt='User profile image'/>
<textarea id='newPostForm' onChange={changeFormHeight}/>
</div>
<div className={styles.NewPostToolbar}>
<p></p>
<button>Post</button>
</div>
</div>
<div className='ContentContainer'>
<Post/>
</div>
</>
)
}
Home.getLayout = function getLayout(page: ReactElement){
return(
<MainLayout>
{page}
</MainLayout>
)
}
export default Home

Next.js | How to improve seo when doing dynamic routing with ssr

I am writing dynamic routes code in, like the code below. So my question is, this dynamic routes page is not reflected in google at all. I have dynamically set the title of this page, but when I search for that title in google, it doesn't show up. What should I do?
src/pages/salon/[[...salon]].js (abridged edition)
import React from 'react'
// Firebase
import { db } from "../../../firebaseConfig"
import { doc, getDoc, updateDoc, arrayUnion, arrayRemove } from "firebase/firestore"
import { getAuth, onAuthStateChanged } from "firebase/auth"
const Salon = ({ id, tag, data }) => {
return (
<>
<NextSeo
title={data.title}
description={data.description}
url={data.url}
canonical={data.url}
openGraph={{
url: data.url,
title: data.title,
description: data.description,
type: "article",
images: [
{
url: data.mainImages[1],
width: 800,
height: 600,
alt: "Image",
}],
}}
/>
<div>{data.salonName}【{data.salonNameKana}】</div>
</>
)
}
export default Salon
export async function getServerSideProps(context) {
const url = context.params.salon
const salonRef = doc(db, "salons", url[0])
const salonSnap = await getDoc(salonRef)
const data = salonSnap.data()
if (!data) {
return {
redirect: {
destination: '/',
permanent: false,
},
}
}
return {
props: {
id: url[0],
tag: url[1] ? url[1] : '',
data: data
}
};
}
src/pages/salon/[[...salon]].js (full edition)
import React, { useEffect } from "react"
import Link from 'next/link'
import { useRouter } from 'next/router'
import Image from "next/image"
// firebase関連
import { db } from "../../firebaseConfig"
import {
collection,
orderBy,
query,
getDocs,
limit,
where
} from 'firebase/firestore'
// fontawesome
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faSearch, faMapMarkedAlt, faSubway } from "#fortawesome/free-solid-svg-icons"
// Redux関連
import { useSelector } from 'react-redux'
import { NextSeo } from "next-seo"
const Index = ({newSalonList}) => {
const router = useRouter()
const area = useSelector((state) => state.searchKey.area)
return (
<div className="Top">
<NextSeo />
{/* 検索 */}
<div className="search">
<div className="reserveText">イメコンサロンを検索する</div>
<div className="area">
<p>{area}</p>
<Link href="area">
<a><button>エリア変更</button></a>
</Link>
</div>
<div className="form">
<form action="/search" method="GET" className="search-box">
<input type="text" name="query" placeholder="サロン名・エリア・メニューなどから検索" />
<button type="submit" value=" 検索"><FontAwesomeIcon icon={faSearch} /></button>
</form>
</div>
<div className="common_relative_image mainImg">
<Image
src='/imecon-personal-color.jpeg'
alt="mainTopImg"
className="mainImg"
layout="fill"
objectFit="contain"
priority
/>
</div>
<div className="searchWay">
<button className="searchWayItem" onClick={(e) =>{
e.preventDefault()
router.push({
pathname: "/area",
// 検索する地域をクリックした後の遷移先のURLを渡す
query: {to:'area-search'}
})
}}>
<div className="areaImage common_relative_image">
<Image
src='/JapanMap.svg'
alt="japanMap"
layout="fill"
objectFit="contain"
/>
</div>
<p><span>エリア</span><br />から探す</p>
</button>
<Link href="station">
<a className="searchWayItem" style={{cursor: "default", background: "#D9D9D9"}} onClick={ (event) => event.preventDefault() }>
<FontAwesomeIcon icon={faSubway} />
<p><span>駅</span>から探す</p>
<small style={{color: "red"}}>coming soon</small>
</a>
</Link>
<Link href="location">
<a className="searchWayItem" style={{cursor: "default", background: "#D9D9D9"}} onClick={ (event) => event.preventDefault() }>
<FontAwesomeIcon icon={faMapMarkedAlt} />
<p><span>現在地</span><br />から探す</p>
<small style={{color: "red"}}>coming soon</small>
</a>
</Link>
</div>
</div>
{/* ブックマークから探す */}
<Link href="/bookmark">
<a className="bookMark">
<svg xmlns="http://www.w3.org/2000/svg" width="24" fill="#FF0100" height="24" viewBox="0 0 24 24"><path d="M12 4.248c-3.148-5.402-12-3.825-12 2.944 0 4.661 5.571 9.427 12 15.808 6.43-6.381 12-11.147 12-15.808 0-6.792-8.875-8.306-12-2.944z"/></svg>
ブックマークから探す
</a>
</Link>
{/* お知らせ */}
<div className="information">
<div className="common_title">| Information</div>
<ul>
<li>
<Link href="/tosalons">
<a>掲載をご希望のサロン様はこちら</a>
</Link>
</li>
</ul>
</div>
{/* 新着サロン */}
<div className="newSalon">
<div className="common_title">| 新着のサロン</div>
<ul>
{newSalonList.map((data) => {
return (
// 新着順のサロンを表示:useEffectで格納したデータ
<li className="newSalonMain" key={data.salonId}>
<Link href={'/salon/' + data.salonId}>
<a className="newSalonMainList">
<div className="newSalonMainListImage common_relative_image">
<Image
src={data.mainImageUrl}
alt="salonMainImage"
layout="fill"
objectFit="contain"
/>
</div>
<div className="newSalonMainListInfo">
<p className="salonName">{data.salonName}{data.salonNameKana}</p>
<div className="salonTraffic">
<div className="common_relative_image salonTrafficImg">
<Image
src='/traffic.svg'
alt="traffic"
layout="fill"
objectFit="contain"
/>
</div>
<span>{data.address}</span>
</div>
<div className="newSalonMainListInfoMenu">
<div className="newSalonMainListInfoMenuTitle">{data.title}</div>
<div className="newSalonMainListInfoMenuPrice">¥{data.price}</div>
</div>
</div>
</a>
</Link>
</li>
)
})}
</ul>
</div>
</div>
)
}
export default Index
export async function getServerSideProps() {
// firestoreから新着順サロンのドキュメントを5つ取得
const salons = query(collection(db, "salons"), where('publish', "==", true), orderBy('createAt', "desc"), limit(5))
const querySnapshot = await getDocs(salons)
// 一旦useEffect内で配列を作り、格納する。後でこの配列をuseStateに格納
let subSalonList = []
// 取得した新着順サロンのドキュメントから情報を取得し、subSalonListに格納
await querySnapshot.forEach((doc) => {
subSalonList = [...subSalonList, {
salonId: doc.id,
salonName: doc._document.data.value.mapValue.fields.salonName.stringValue,
salonNameKana: doc._document.data.value.mapValue.fields.salonNameKana ? '【' + doc._document.data.value.mapValue.fields.salonNameKana.stringValue + '】': '',
address: doc._document.data.value.mapValue.fields.access ? doc._document.data.value.mapValue.fields.access.stringValue: '',
mainImageUrl: doc._document.data.value.mapValue.fields.mainImages.mapValue.fields[1].stringValue,
}]
})
// サブコレクションの情報は↑で一緒に取得できなかったので、↓で格納する。subSalonListをループして、salonIdを元に順番に情報を取得し、subSalonListに格納している。
for (const value of subSalonList) {
try {
// salonIdを元にmenuサブコレクション下のドキュメントでtopがtrueのものを検索&取得
const q = query(collection(db, "salons", value.salonId, "menu"))
const querySnapshot2 = await getDocs(q)
value.title = querySnapshot2.docs[0]._document.data.value.mapValue.fields.title.stringValue
value.price = querySnapshot2.docs[0]._document.data.value.mapValue.fields.price.integerValue.toLocaleString()
value.time = querySnapshot2.docs[0]._document.data.value.mapValue.fields.time.integerValue.toLocaleString()
} catch(err) {
console.log(err)
return 'err'
}
}
return {
props: {
newSalonList: subSalonList,
}
}
}
site meta tag
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"><link rel="icon" href="https://kanunu-beauty.com/favicon.ico">
<link rel="apple-touch-icon" href="https://kanunu-beauty.com/favicon.ico">
<title>Kanunu(カヌヌ)|パーソナルカラー・骨格診断・顔タイプ診断などのイメコンサロンが探せる日本最大級の検索サイト</title>
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="#kanunu_official">
<meta name="twitter:creator" content="#kanunu_official">
<meta property="og:image:alt" content="Kanunu_Image">
<meta property="og:image:width" content="800">
<meta property="og:image:height" content="600">
<meta property="og:locale" content="ja_JP"><meta property="og:site_name" content="Kanunu(カヌヌ)"><link rel="icon" href="https://kanunu-beauty.com/favicon.ico"><link rel="apple-touch-icon" href="https://kanunu-beauty.com/favicon.ico" sizes="76x76"><meta name="robots" content="index,follow">
<meta property="og:image" content="https://kanunu-beauty.com/kanunu.png">
<meta name="description" content="パーソナルカラー・骨格診断・顔タイプ診断などのイメコンサロンが探せる日本最大級の検索サイト。24時間いつでもネット検索OK。イメージコンサルティングを検索するならKanunu">
<meta property="og:title" content="Kanunu(カヌヌ)|パーソナルカラー・骨格診断・顔タイプ診断などのイメコンサロンが探せる日本最大級の検索サイト">
<meta property="og:description" content="パーソナルカラー・骨格診断・顔タイプ診断などのイメコンサロンが探せる日本最大級の検索サイト。24時間いつでもネット検索OK。イメージコンサルティングを検索するならKanunu">
<meta property="og:url" content="https://kanunu-beauty.com/">
<meta property="og:type" content="website">
<link rel="canonical" href="https://kanunu-beauty.com/">
<link rel="preload" as="image" imagesrcset="/_next/image?url=%2Fimecon-personal-color.jpeg&w=640&q=75 640w, /_next/image?url=%2Fimecon-personal-color.jpeg&w=750&q=75 750w, /_next/image?url=%2Fimecon-personal-color.jpeg&w=828&q=75 828w, /_next/image?url=%2Fimecon-personal-color.jpeg&w=1080&q=75 1080w, /_next/image?url=%2Fimecon-personal-color.jpeg&w=1200&q=75 1200w, /_next/image?url=%2Fimecon-personal-color.jpeg&w=1920&q=75 1920w, /_next/image?url=%2Fimecon-personal-color.jpeg&w=2048&q=75 2048w, /_next/image?url=%2Fimecon-personal-color.jpeg&w=3840&q=75 3840w" imagesizes="100vw">
<meta name="next-head-count" content="24">
<meta charset="UTF-8">
<meta name="theme-color" content="#ff889e"><script src="https://apis.google.com/_/scs/abc-static/_/js/k=gapi.lb.ja.5EzdJRYYqVI.O/m=gapi_iframes/rt=j/sv=1/d=1/ed=1/rs=AHpOoo_YkgRfk9NA-tU81xmKmFrifcc8Yg/cb=gapi.loaded_0?le=scs" async=""></script><script src="https://partner.googleadservices.com/gampad/cookie.js?domain=kanunu-beauty.com&callback=_gfp_s_&client=ca-pub-3226715501424001&cookie=ID%3Da210217fc7bc19a0-226fbd731ed100ba%3AT%3D1648307385%3ART%3D1648307385%3AS%3DALNI_Mbl4zhbFc5KZH7kiBhF4bogpQHECQ"></script><script src="https://pagead2.googlesyndication.com/pagead/managed/js/adsense/m202205050101/show_ads_impl_fy2019.js?bust=31067451" id="google_shimpl"></script><script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3226715501424001" crossorigin="anonymous" data-checked-head="true"></script><link rel="preload" href="/_next/static/css/3af99d7a8bc648e8.css" as="style"><link rel="stylesheet" href="/_next/static/css/3af99d7a8bc648e8.css" data-n-g=""><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-5cd94c89d3acac5f.js"></script><script src="/_next/static/chunks/webpack-5752944655d749a0.js" defer=""></script><script src="/_next/static/chunks/framework-5f4595e5518b5600.js" defer=""></script><script src="/_next/static/chunks/main-958eae9894028eb3.js" defer=""></script><script src="/_next/static/chunks/pages/_app-7066ec57ee9a283b.js" defer=""></script><script src="/_next/static/chunks/675-a0e7bc4ebaaecba0.js" defer=""></script><script src="/_next/static/chunks/pages/index-b8f101c1b5c4520a.js" defer=""></script><script src="/_next/static/c6OFukToh53vEUvjEggAL/_buildManifest.js" defer=""></script><script src="/_next/static/c6OFukToh53vEUvjEggAL/_ssgManifest.js" defer=""></script><script src="/_next/static/c6OFukToh53vEUvjEggAL/_middlewareManifest.js" defer=""></script>
<meta http-equiv="origin-trial" content="AxujKG9INjsZ8/gUq8+dTruNvk7RjZQ1oFhhgQbcTJKDnZfbzSTE81wvC2Hzaf3TW4avA76LTZEMdiedF1vIbA4AAABueyJvcmlnaW4iOiJodHRwczovL2ltYXNkay5nb29nbGVhcGlzLmNvbTo0NDMiLCJmZWF0dXJlIjoiVHJ1c3RUb2tlbnMiLCJleHBpcnkiOjE2NTI3NzQ0MDAsImlzVGhpcmRQYXJ0eSI6dHJ1ZX0=">
<meta http-equiv="origin-trial" content="Azuce85ORtSnWe1MZDTv68qpaW3iHyfL9YbLRy0cwcCZwVnePnOmkUJlG8HGikmOwhZU22dElCcfrfX2HhrBPAkAAAB7eyJvcmlnaW4iOiJodHRwczovL2RvdWJsZWNsaWNrLm5ldDo0NDMiLCJmZWF0dXJlIjoiVHJ1c3RUb2tlbnMiLCJleHBpcnkiOjE2NTI3NzQ0MDAsImlzU3ViZG9tYWluIjp0cnVlLCJpc1RoaXJkUGFydHkiOnRydWV9">
<meta http-equiv="origin-trial" content="A16nvcdeoOAqrJcmjLRpl1I6f3McDD8EfofAYTt/P/H4/AWwB99nxiPp6kA0fXoiZav908Z8etuL16laFPUdfQsAAACBeyJvcmlnaW4iOiJodHRwczovL2dvb2dsZXRhZ3NlcnZpY2VzLmNvbTo0NDMiLCJmZWF0dXJlIjoiVHJ1c3RUb2tlbnMiLCJleHBpcnkiOjE2NTI3NzQ0MDAsImlzU3ViZG9tYWluIjp0cnVlLCJpc1RoaXJkUGFydHkiOnRydWV9">
<meta http-equiv="origin-trial" content="AxBHdr0J44vFBQtZUqX9sjiqf5yWZ/OcHRcRMN3H9TH+t90V/j3ENW6C8+igBZFXMJ7G3Pr8Dd13632aLng42wgAAACBeyJvcmlnaW4iOiJodHRwczovL2dvb2dsZXN5bmRpY2F0aW9uLmNvbTo0NDMiLCJmZWF0dXJlIjoiVHJ1c3RUb2tlbnMiLCJleHBpcnkiOjE2NTI3NzQ0MDAsImlzU3ViZG9tYWluIjp0cnVlLCJpc1RoaXJkUGFydHkiOnRydWV9">
<meta http-equiv="origin-trial" content="A88BWHFjcawUfKU3lIejLoryXoyjooBXLgWmGh+hNcqMK44cugvsI5YZbNarYvi3roc1fYbHA1AVbhAtuHZflgEAAAB2eyJvcmlnaW4iOiJodHRwczovL2dvb2dsZS5jb206NDQzIiwiZmVhdHVyZSI6IlRydXN0VG9rZW5zIiwiZXhwaXJ5IjoxNjUyNzc0NDAwLCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ==">
<meta http-equiv="origin-trial" content="AzoawhTRDevLR66Y6MROu167EDncFPBvcKOaQispTo9ouEt5LvcBjnRFqiAByRT+2cDHG1Yj4dXwpLeIhc98/gIAAACFeyJvcmlnaW4iOiJodHRwczovL2RvdWJsZWNsaWNrLm5ldDo0NDMiLCJmZWF0dXJlIjoiUHJpdmFjeVNhbmRib3hBZHNBUElzIiwiZXhwaXJ5IjoxNjYxMjk5MTk5LCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ==">
<meta http-equiv="origin-trial" content="A6+nc62kbJgC46ypOwRsNW6RkDn2x7tgRh0wp7jb3DtFF7oEhu1hhm4rdZHZ6zXvnKZLlYcBlQUImC4d3kKihAcAAACLeyJvcmlnaW4iOiJodHRwczovL2dvb2dsZXN5bmRpY2F0aW9uLmNvbTo0NDMiLCJmZWF0dXJlIjoiUHJpdmFjeVNhbmRib3hBZHNBUElzIiwiZXhwaXJ5IjoxNjYxMjk5MTk5LCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ==">
<meta http-equiv="origin-trial" content="A/9La288e7MDEU2ifusFnMg1C2Ij6uoa/Z/ylwJIXSsWfK37oESIPbxbt4IU86OGqDEPnNVruUiMjfKo65H/CQwAAACLeyJvcmlnaW4iOiJodHRwczovL2dvb2dsZXRhZ3NlcnZpY2VzLmNvbTo0NDMiLCJmZWF0dXJlIjoiUHJpdmFjeVNhbmRib3hBZHNBUElzIiwiZXhwaXJ5IjoxNjYxMjk5MTk5LCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ=="><link as="script" rel="prefetch" href="/_next/static/chunks/pages/mypage-141f665c92390596.js"><link as="script" rel="prefetch" href="/_next/static/chunks/733-e7d84bed7ed356fa.js"><link as="script" rel="prefetch" href="/_next/static/chunks/pages/bookmark-61fff4ba51ad8155.js"><link as="script" rel="prefetch" href="/_next/static/chunks/866-c05af8e23c872e2c.js"><link as="script" rel="prefetch" href="/_next/static/chunks/pages/signin-ae60a19128cbc932.js"><link as="script" rel="prefetch" href="/_next/static/chunks/932-f01e4d1c2aeb2e91.js"><link as="script" rel="prefetch" href="/_next/static/chunks/pages/area-bf69ae687aa602d8.js"><link as="script" rel="prefetch" href="/_next/static/chunks/pages/tosalons-79196977b12f1828.js"><link as="script" rel="prefetch" href="/_next/static/chunks/pages/salon/%5B%5B...salon%5D%5D-15ea0d621d053ed1.js"><link rel="preload" href="https://adservice.google.co.jp/adsid/integrator.js?domain=kanunu-beauty.com" as="script"><script type="text/javascript" src="https://adservice.google.co.jp/adsid/integrator.js?domain=kanunu-beauty.com"></script><link rel="preload" href="https://adservice.google.com/adsid/integrator.js?domain=kanunu-beauty.com" as="script"><script type="text/javascript" src="https://adservice.google.com/adsid/integrator.js?domain=kanunu-beauty.com"></script><script src="https://apis.google.com/js/api.js?onload=__iframefcb771037" type="text/javascript" charset="UTF-8" gapi_processed="true"></script><link as="script" rel="prefetch" href="/_next/static/chunks/pages/privacy-policy-244018d019bd23ac.js"><link as="script" rel="prefetch" href="/_next/static/chunks/pages/terms-of-service-6bb94404533a0249.js">
</head>
_app.js
import React, { useEffect } from 'react'
import { useRouter } from 'next/router'
import Script from 'next/script'
import Head from 'next/head'
import * as gtag from '../lib/gtag.js'
import { GA_TRACKING_ID } from '../lib/gtag'
import { Provider } from "react-redux"
import redux from '../components/store/redux.js'
import '../../public/styles/Common.scss'
import '../../public/styles/Top.scss'
import '../../public/styles/index.scss'
import '../../public/styles/Header.scss'
import '../../public/styles/Footer.scss'
import '../../public/styles/ToSalons.scss'
import '../../public/styles/Terms.scss'
import '../../public/styles/Area.scss'
import '../../public/styles/Salon.scss'
import '../../public/styles/Search.scss'
import '../../public/styles/Bookmark.scss'
import '../../public/styles/SignUp.scss'
import '../../public/styles/SignUp.scss'
import '../../public/styles/Mypage.scss'
import '../../public/styles/SignUp.scss'
import '../../public/styles/SignUp.scss'
import "../../public/styles/Pagination.scss"
import Header from '../components/block/Header.js'
import Footer from '../components/block/Footer.js'
import { DefaultSeo } from 'next-seo'
import SEO from '../../next-seo.config'
import { config } from '#fortawesome/fontawesome-svg-core'
import '#fortawesome/fontawesome-svg-core/styles.css'
config.autoAddCss = false
export default function App({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleComplete = (url) => {
// google analyticsの設定
gtag.pageview(url)
};
router.events.on("routeChangeComplete", handleComplete);
router.events.on("routeChangeError", handleComplete);
return () => {
router.events.off('routeChangeComplete', handleComplete)
}
},[router])
return (
<>
<Head>
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
<link rel="icon" href="https://test.com/favicon.ico" />
<link rel="apple-touch-icon" href="https://test.com/favicon.ico" />
</Head>
<DefaultSeo {...SEO} />
{/* Analytics */}
<Script
async
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
<Script
id="analytics"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`,
}}
/>
<Provider store={redux}>
<Header />
<Component {...pageProps} />
<Footer />
</Provider>
</>
)
}
_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document';
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render() {
return (
<Html lang="ja">
<Head>
<meta charSet="UTF-8" />
<meta name="theme-color" content="#ff889e" />
{/* google広告 */}
<script async
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=xxxxxxxxxxx"
crossOrigin="anonymous"></script>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;

Custom class sent via props getting overridden on production build [using nextjs with react-jss]

I'm trying to pass a custom class using props to another component. It works absolutely fine on the dev mode but gets overridden on the production build.
Input Component
import React, { memo } from "react"
import injectSheet from "react-jss"
import styles from "./styles"
const Input = React.forwardRef((props, ref) => {
const {
classes,
value,
disabled,
type = "text",
accept,
placeholder,
required,
customClass = "",
pattern,
} = props
const handleChange = (e) => {
/* Logic */
}
return (
<input
ref={ref}
className={`${classes.container} ${customClass}`}
type={type}
accept={accept}
placeholder={placeholder}
value={value}
onChange={handleChange}
required={required}
disabled={disabled}
pattern={pattern}
/>
)
})
export default injectSheet(styles)(memo(Input))
Usage
<Input
type="email"
placeholder="Enter your email"
customClass={classes.customEmailInput}
onChange={setEmail}
highlight={emailError || isDisposableMail}
/>
Happening on production build
Here the customEmailInput class gets overridden
Moreover, I'm seeing some inconsistency with the class names, some classes appear with the prefix "Component" while some classes have the component name as the prefix. First reference Second reference. If I remove the forwardRef and memo from the Input component, then the class names get prefixed with the component name. Third reference
_app.js
import React from "react"
import Head from "next/head"
import { QueryClient, QueryClientProvider } from "react-query"
import { ThemeProvider } from "react-jss"
import { generateTheme } from "../theme"
import Header from "../components/shared/header"
const queryClient = new QueryClient()
const MyApp = (props) => {
const { Component, pageProps } = props
return (
<>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Title</title>
</Head>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={generateTheme()}>
<Header />
<Component {...pageProps} />
</ThemeProvider>
</QueryClientProvider>
</>
)
}
export default MyApp
_document.js
import Document, { Html, Head, Main, NextScript } from "next/document"
import { SheetsRegistry, JssProvider, createGenerateId } from "react-jss"
class MyDocument extends Document {
static async getInitialProps(ctx) {
const registry = new SheetsRegistry()
const generateId = createGenerateId()
const originalRenderPage = ctx.renderPage
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
(
<JssProvider registry={registry} generateId={generateId}>
<App {...props} />
</JssProvider>
),
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: (
<>
{initialProps.styles}
<style
id="server-side-styles"
dangerouslySetInnerHTML={{ __html: registry.toString() }}
/>
</>
),
}
}
render() {
return (
<Html>
<Head>
<link rel="icon" href="/images/favicon.ico" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument

Failed to Export Default and Another Function

Plan - To render <List /> element in index.js. Displays the todo items the user has created.
Error -
./src/components/App.jsx
Attempted import error: './List' does not contain a default export (imported as 'List').
index.js -
import React from 'react';
import ReactDOM from 'react-dom';
import {List, Render} from './components/List';
import App from './components/App';
import '../src/styles.css';
ReactDOM.render(
<App />,
document.getElementById('root')
);
ReactDOM.render(
<Render />
, document.getElementById("list"));
List.jsx -
import react, { useRef } from 'react';
import ReactDOM from 'react-dom';
var todoItems = [];
const inputRef = useRef();
function onClick() {
todoItems.push(inputRef.current.value);
console.log("Pushed item in the array!");
render(inputRef.current.value);
}
function Render(value) {
todoItems.forEach(function a(item) {
<h1>{item}</h1>
});
}
function List() {
return (
<div className="mainbox">
<div className="inputdiv">
<input
type="text"
ref={inputRef}
placeholder="Enter Task..."
className="textbox"
id="taskName"
/>
<button className="button" onClick={onClick}>+</button>
</div>
</div>
);
}
export {List, Render};
I also tried -
export default List;
export {Render};
But it says useRef() cannot be called at the top level.
So I moved the inputRef to the List(), but it says that Render isn't defined.
Thanks!
P.S
After this import/export problem is solved, will the <h1> display?
function Render(value) {
todoItems.forEach(function a(item) {
<h1>{item}</h1>
});
}
EDIT -
index.html -
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link font-family: "Montserrat" , sans-serif;
href="https://fonts.googleapis.com/css?family=McLaren|Montserrat&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="../src/styles.css">
<title>Mandy's Todo-List App!</title>
</head>
<body>
<div id="root">
<div id="list">
</div>
</div>
</body>
</html>
import { useState, useEffect } from 'react';
function App() {
const [value, setValue] = useState('');
const [list, setList] = useState([]);
function handleChange(event) {
setValue(event.target.value);
}
function addTodo() {
setList([...list, value]);
}
useEffect(() => setValue(''), [list]);
return (
<div className='App'>
<input value={value} onChange={handleChange}/>
<button onClick={addTodo}>Add</button>
{list.map((item, index) => <h1 key={index}>{item}</h1>)}
</div>
);
}
export default App;

Why is 'emailjs' is not defined no-undef?

I am following the Emailjs tutorial found here - https://www.emailjs.com/docs/tutorial/creating-contact-form/.
I am getting these error lines when the web page fails to compile:
Failed to compile./src/components/SwatForm.js
Line 42:29: 'emailjs' is not defined no-undef
Line 52:33: 'emailjs' is not defined no-undef
Line 64:33: 'emailjs' is not defined no-undef
Search for the keywords to learn more about each error.
This error occurred during the build time and cannot be dismissed.
Here is the actual code from the tutorial in my react application.
import React, { Component } from 'react';
import { Container, Row, Col, Button, NavLink } from 'reactstrap';
import { Link } from 'react-router-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import groupPicture from "../church2019.jpg";
import Image from 'react-bootstrap/Image';
import './Site.css';
export class SwatForm extends Component {
static displayName = SwatForm.name;
constructor(props) {
super(props);
this.state = {
name: "",
email: "",
feedback: "",
};
}
handleInputChange(event) {
event.preventDefault();
const target = event.target;
const name = target.name;
const value = target.value;
this.setState({ [name]: value });
}
render() {
return (
<Container>
<html>
<head>
<title>Contact Form</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/emailjs-com#2/dist/email.min.js"></script>
<script type="text/javascript">
(function() {
// https://dashboard.emailjs.com/admin/integration
emailjs.init('user_0rJrw0xxhrNqhld3WUc3q')
})();
</script>
<script type="text/javascript">
window.onload = function() {
document.getElementById('contact-form').addEventListener('submit', function (event) {
event.preventDefault();
// generate a five digit number for the contact_number variable
this.contact_number.value = Math.random() * 100000 | 0;
// these IDs from the previous steps
emailjs.sendForm('contact_service', 'contact_form', this)
.then(function () {
console.log('SUCCESS!');
}, function (error) {
console.log('FAILED...', error);
});
})
}
</script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/emailjs-com#2/dist/email.min.js"></script>
<script type="text/javascript">
(function() {
emailjs.init("user_0rJrw0xxhrNqhld3WUc3q")
})();
</script>
</head>
<form id="contact-form">
<input type="hidden" name="contact_number" />
<label>Name</label>
<input type="text" name="user_name" />
<label>Email</label>
<input type="email" name="user_email" />
<label>Message</label>
<textarea name="message"></textarea>
<input type="submit" value="Send" />
</form>
</html>
</Container>
);
}
}
There is a package for the use of this library. I think you should use it.
https://www.npmjs.com/package/emailjs-com
https://www.emailjs.com/docs/examples/reactjs/

Resources