I've been trying to look for answers to this one and am coming up blank. The goal is to have user auth go through on a form, that is in a modal. All of the UI stuff works great, until I try and apply the Auth. I am not sure how I would have ended up with mismatching versions of React, so I do not believe that to be the problem. What can be done about this?
const LoginModal = (props) => {
const [loginData, setLoginData] = useState({ email: "", password: "" });
const [validated] = useState(false);
const [login, { error }] = useMutation(LOGIN_USER);
const userInputHandler = (event) => {
const { name, value } = event.target;
setLoginData({ ...loginData, [name]: value });
};
const handleSubmit = async (event) => {
event.preventDefault();
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
try {
const { data } = await login({
variables: { ...loginData },
});
Auth.login(data.login.token);
} catch (err) {
console.error(err);
}
setLoginData({
username: "",
email: "",
password: "",
});
};
return (
<div>
<div className="flex items-center justify-center md:z-100 min-h-screen bg-rose-50">
<form noValidate validated={validated} onSubmit={handleSubmit}>
<div className="relative flex flex-col m-6 space-y-10 bg-white shadow-2xl rounded-2xl md:flex-row md:space-y-0 md:m-0">
{/* left side */}
<div className="p-6 md-20">
{/* top content */}
<h2 className="h2 mb-5 text-4xl font-bold cursor-pointer">
Login
</h2>
<p className="max-w-sm mb-12 font-light text-gray-600">
Login to see your saved activities!
</p>
<div className="space-y-4">
<input
type="text"
className="w-full space-y-4 p-6 border border-gray-300 rounded-md placeholder:font-light "
placeholder="Email"
onChange={userInputHandler}
value={loginData.email}
/>
<input
type="text"
className="w-full space-y-4 p-6 border border-gray-300 rounded-md placeholder:font-light "
placeholder="Password"
onChange={userInputHandler}
value={loginData.password}
/>
</div>
{/* Middle Content */}
<div className="flex flex-col items-center justify-between mt-6 space-y-6 md:flex-row md:space-y-0">
<div
className="font-thin text-cyan-700 cursor-pointer"
onClick={props.SwitchModal}
>
Sign Up
</div>
<button disabled={!loginData.email && loginData.password} className="w-full md:w-auto flex justify-center items-center p-6 space-x-4 font-bold text-white rounded-md px-9 bg-cyan-700 shadow-cyan-100 hover:bg-opacity-90 shadow-sm hover:shadow-lg border transition hover:-translate-y-0.5 duration-150 ">
<span>Next</span>
<FaHandPointRight />
</button>
</div>
{/* Border */}
<div className="mt-12 border-b border-b-gray-300"> </div>
{/* Bottom Content */}
<p className="py-6 text-sm font-thin text-center text-gray-400">
You can also login with
</p>
<div className="flex flex-col space-x-0 space-y-6 md:flex-row md:space-x-4 md:space-y-0">
<button className="flex items-center justify-center py-2 space-x-3 border border-gray-300 rounded shadow-sm hover:bg-opacity-30 hover:shadow-lg hover:-translate-y-0.5 transition duration-150 md:w-1/2 ">
<FaFacebook />
<span className="font-thin">Facebook</span>
</button>
<button className="flex items-center justify-center py-2 space-x-3 border border-gray-300 rounded shadow-sm hover:bg-opacity-30 hover:shadow-lg hover:-translate-y-0.5 transition duration-150 md:w-1/2 ">
<FcGoogle />
<span className="font-thin">Google</span>
</button>
</div>
</div>
{/* right side */}
<img src={Chairman} alt="" className="w-[430px] hidden md:block" />
{/* Close button */}
<div className="group absolute -top-5 right-4 md:top-4 hover:cursor-pointer hover:-translate-y-0.5 transition duration-150">
<FaWindowClose
onClick={props.onShowModal}
className="group-hover:text-gray-600"
/>
</div>
</div>
</form>
</div>
</div>
);
};
export default LoginModal;
Mutations:
import { gql } from "#apollo/client";
export const LOGIN_USER = gql`
mutation login($email: String!, $password: String!) {
login(email: $email, password: $password) {
token
user {
_id
username
}
}
}
`;
export const ADD_USER = gql`
mutation addUser($username: String!, $email: String!, $password: String!) {
addUser(username: $username, email: $email, password: $password) {
token
user {
_id
username
email
}
}
}
`;
export const SAVE_TASK = gql`
mutation saveTask($input: SavedTaskInput) {
saveTask(input: $input) {
_id
username
email
savedTasks {
activity
type
participants
price
link
key
accessibility
}
}
}
`;
export const REMOVE_TASK = gql`
mutation removeTask($key: String!) {
removeTask(key: $key) {
_id
username
email
savedTasks {
activity
type
participants
price
link
key
accessibility
}
}
}
`;
Related
I'm having a problem here when creating a remember me function in Reactjs.
So, I managed to enter the username, password and isChecked into localstorage.
However, when I logged out, all the data in the local storage was deleted including the username, password and isChecked.
How do you make the username, password and isChecked persist after logging out? Thank You
MyCode =
import React, { useState } from "react";
import Logo from "../../../../assets/images/logo.png";
import BackgroundProfile from "../../../../assets/images/background-profile.png";
import { VscKey } from "react-icons/vsc";
import { Link } from "react-router-dom";
import { signin } from "../../../../service/account";
import { toast } from "react-toastify";
export default function CardLogin(props) {
const {
labelText,
inputText,
loginType,
firstOptionLogin,
secondOptionLogin,
labelIcon,
firstRoute,
secondRoute,
firstIcon,
secondIcon,
} = props;
const [username, setUsername] = useState(() =>
localStorage.checkbox ? localStorage.username : ""
);
const [password, setPassword] = useState(() =>
localStorage.checkbox ? localStorage.password : ""
);
const [isChecked, setIsChecked] = useState(() => !!localStorage.checkbox);
const initialValue = {
username: username,
password: password,
nik: "",
};
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = () => {
if (isChecked && username !== "") {
localStorage.username = username;
localStorage.password = password;
localStorage.checkbox = isChecked ? "1" : "";
}
const data = new URLSearchParams();
console.log(loginType);
if (loginType === "username") {
data.append("username", initialValue.username);
data.append("password", initialValue.password);
data.append("grant_type", "password");
}
setIsLoading(true);
signin(data, loginType)
.then((response) => {
if (response?.code === 200) {
console.log(response);
localStorage.setItem(
"accessToken",
JSON.stringify(response.data.accessToken)
);
localStorage.setItem(
"userSession",
JSON.stringify(response.data.accessTokenExpiresAt)
);
window.location.reload();
} else {
toast.error(response.message);
}
})
.catch((error) => {
console.log(error);
})
.finally(() => {
setIsLoading(false);
});
};
return (
<div
style={{
backgroundSize: "100% 75%",
backgroundRepeat: "no-repeat",
height: "100%",
backgroundImage: `url(${BackgroundProfile})`,
}}
>
{/* Button Back on The Top of Page */}
<button>
<div className="rounded-full w-5 md:w-10 h-5 md:h-10 p-3 relative top-2 left-2">
{/* <FaArrowLeft className="text-[#DD2729] text-xs md:text-base absolute top-1/2 left-1/2 -translate-y-1/2 -translate-x-1/2" /> */}
</div>
</button>
{/* Logo & Card Login */}
<img
src={Logo}
className="absolute top-6 bottom-6 right-4 w-2/5 md:w-1/5"
alt="Logo"
/>
<div className="mx-auto mt-8 w-3/5 md:w-2/5 bg-white px-5 py-3 md:px-8 md:py-5 rounded-md drop-shadow-xl">
<button>
{/* <FaArrowLeft className="text-[#DD2729] text-xs md:text-base" /> */}
</button>
<article
style={{ fontFamily: "ubuntu" }}
className="text-center text-[#808285] text-xs md:text-lg"
>
Selamat Datang! <br /> Silakan masuk untuk mulai menggunakan aplikasi
</article>
{/* Form Login*/}
<form onSubmit={handleSubmit} className="pt-3 md:pt-5">
<div>
<label
htmlFor={loginType}
className="text-[#424242] text-xs md:text-sm "
>
{labelText}
</label>
<div className="flex">
<div
className={`w-10 flex items-center justify-center bg-blue-lighter border-y border-l border-[#9E9E9E] rounded-l text-blue-dark`}
>
{labelIcon}
</div>
<input
id="username"
name="username"
type="text"
placeholder={inputText}
value={username}
onChange={(e) => setUsername(e.target.value)}
className={`w-full border-l-0 border-[#9E9E9E] rounded-r text-xs md:text-lg font-sans`}
/>
</div>
</div>
<div className="pt-2 md:pt-3">
<label
htmlFor="password"
className="text-[#424242] text-xs md:text-sm"
>
Password
</label>
<div className="flex">
<div
className={`w-10 flex items-center justify-center bg-blue-lighter border-y border-l border-[#9E9E9E] rounded-l text-blue-dark `}
>
<VscKey className="text-[#A8A8A8] text-xl" />
</div>
<input
id="password"
name="password"
type="password"
placeholder="Masukkan kata sandi anda"
value={password}
onChange={(e) => setPassword(e.target.value)}
className={`w-full border-l-0 border-[#9E9E9E] rounded-r text-xs md:text-lg font-normal `}
/>
</div>
</div>
{/* End of Form Login */}
{/* Remember me & Forgot Password */}
<div className="flex pt-3 items-center justify-between">
<div className="flex items-center gap-1.5">
<input
type="checkbox"
checked={isChecked}
name="lsRememberMe"
onChange={(e) => setIsChecked(e.target.checked)}
className="rounded"
/>
<label htmlFor="" className="text-xs md:text-sm">
Remember me
</label>
</div>
<div className="">
<Link
to="/forgot-password"
className="text-xs md:text-sm underline underline-offset-1 font-bold"
>
Forgot password
</Link>
</div>
</div>
<input
type="submit"
value={isLoading ? "Loading" : "Masuk"}
disabled={isLoading}
className="bg-[#EA001E] hover:bg-[#F55151] active:bg-[#BA0D0D] w-full text-white font-bold text-md rounded-lg p-2 mt-4 shadow-xl cursor-pointer text-xs md:text-sm"
/>
</form>
{/* End of Remember me & Forgot Password */}
{/* Login Option */}
<div className="mt-3">
<h4 className="login-selection text-center text-xs md:text-sm">
or login with
</h4>
</div>
<div className="flex items-center justify-center gap-2 mt-4">
<Link to={firstRoute}>
<div className="flex items-center justify-around gap-2 bg-white shadow-xl rounded-full py-2 px-3 md:px-8">
<img
src={firstIcon}
alt="Login Icons"
className="w-2/6 md:w-4/5"
/>
<div className="font-bold text-xs md:text-sm uppercase">
{firstOptionLogin}
</div>
</div>
</Link>
<Link to={secondRoute}>
<div className="flex items-center justify-around gap-2 bg-white shadow-xl rounded-full py-2 px-3 md:px-10">
<img
src={secondIcon}
alt="Login Icons"
className="w-2/6 md:w-4/5"
/>
<div className="font-bold text-xs md:text-sm uppercase">
{secondOptionLogin}
</div>
</div>
</Link>
</div>
{/* End of Login Option */}
{/* Alternative Register */}
<div className="flex items-center justify-center gap-2 mt-4 text-xs md:text-sm">
<p>Don't have an account?</p>
<Link
to="/signup"
className="text-[#DE1B1B] font-bold text-xs md:text-sm"
>
Sign Up
</Link>
</div>
{/* End of Alternative Register */}
</div>
</div>
);
}
CASE CLOSED, in the form section onSubmit={handleSubmit} I delete it to just form, then onSubmit={handleSubmit} I move it to button to become onClick={handleSubmit}.
thank you for answering my question. i appreciate it
Iam trying to fetch the data from this API
https://covid-19-coronavirus-statistics.p.rapidapi.com/v1/total
the data is successfully displayed in the console
but it is not displayed in the browser
const [selectedCountry, setSelectedCountry] = useState('USA');
const [stats, setStats] = useState({});
useEffect(() => {
const fetchData = async () => {
const result = await axios.get(API_URL, {
headers: {
'x-rapidapi-host': 'covid-19-coronavirus-statistics.p.rapidapi.com',
'x-rapidapi-key': 'YOUR_API_KEY',
},
params: {
country: selectedCountry
}
});
setStats(result.data.data);
};
fetchData();
}, [selectedCountry]);
return (
<div className="flex flex-col items-center justify-center p-4">
<div className="select w-full md:w-1/2">
<h2 className="text-xl text-red-600 font-medium mb-3">Select a Country:</h2>
<select value={selectedCountry} onChange={e => setSelectedCountry(e.target.value)} className="block appearance-none w-full bg-white text-blue-500 border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline">
{countries.map(country => (
<option key={country} value={country}>{country}</option>
))}
</select>
</div>
<div className="stats w-full text-blue-600 md:w-1/2 mt-4">
<h2 className="text-xl text-red-600 font-medium mb-3">Selected Country:</h2>
<h2 className='text-yellow-600 mb-4 bg-gray-200'>{selectedCountry}</h2>
<p className="stat">Confirmed Cases: {stats.confirmed}</p>
<p className="stat">Deaths: {stats.deaths}</p>
<p className="stat">Recoveries: {stats.recovered}</p>
</div>
</div>
the result in the console is:
data : confirmed : 4563190 deaths : 50541 lastChecked : "2023-01-31T18:11:16+00:00" lastReported : "2023-01-31T04:20:32+00:00" location : "US" recovered : null
also i picture
Note that ive already used my own API key
So basically when i try to fill the form and exchange between two accounts , nothing happens
and when i check the console sometimes there's no error appearing and sometimes there's this one "Uncaught TypeError: Cannot destructure property 'connectWallet' of 'useContext(...)' as it is undefined." , and there's no data being destructered and sent to the browser , it's empty enter image description here
and it triggers "no accounts found"
this is the console after and click "send"
enter image description here
this is my code:
TransactionContext.jsx :
import React, {useEffect, useState} from 'react';
import {ethers} from 'ethers';
import {contractABI, contractAddress} from '../utils/constants';
export const TransactionContext = React.createContext();
const {ethereum} = window;
const getEthereumContract = () => {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const transactionContract = new ethers.Contract(contractAddress, contractABI, signer);
return transactionContract;
}
export const TransactionProvider = ({children}) => {
const [currentAccount, setCurrentAccount] = useState('');
const [formData, setFormData] = useState({addressTo: '', amount: '', keyword: '', message: ''});
const [isLoading, setIsLoading] = useState(false);
const [transactionCount, setTransactionCount] = useState(localStorage.getItem('transactionCount'));
const handleChange = (e, name) => {
setFormData((prevState) => ({ ...prevState, [name]: e.target.value }));
}
const checkIfWalletIsConnected = async () => {
try{
if(!ethereum) return alert("please install metamask");
const accounts = await ethereum.request({method: 'eth_accounts'});
if(accounts.length) {
setCurrentAccount(accounts[0]);
}
else {
console.log('no accounts found');
}
}
catch (error) {
console.log(error);
throw new Error("No ethereum object.")
}
};
const connectWallet = async () => {
try{if(!ethereum) return alert("please install metamask");
const accounts = await ethereum.request({method: 'eth_requestAccounts'});
setCurrentAccount(accounts[0]);}
catch (error){
console.log(error);
throw new Error("No ethereum object.")
}
}
const sendTransaction = async () => {
try{if(!ethereum) return alert("please install metamask");
const {addressTo, amount, keyword, message } = formData;
const transactionContract = getEthereumContract();
const parseAmount = ethers.utils.parseEther(amount);
await ethereum.request({
method: 'eth_sendTransaction',
params: [{
from: currentAccount,
to: addressTo,
gas: '0x5208',
value: parseAmount._hex,
}]
});
const transactionHash = await transactionContract.addToBlockChain(addressTo, parseAmount, message, keyword);
setIsLoading(true);
console.log('Loading - ${transactionHash.has}');
await transactionHash.wait();
setIsLoading(false);
console.log('success - ${transactionHash.has}');
const transactionCount = await transactionContract.getTransactionCount();
setTransactionCount(transactionCount.toNumber());
}
catch(error) {
console.log(error);
throw new Error("No ethereum object.")
}
}
useEffect( () => {
checkIfWalletIsConnected();
}, []);
return (
<TransactionContext.Provider value={{connectWallet, currentAccount, formData, setFormData, handleChange, sendTransaction}}>
{children}
</TransactionContext.Provider>
);
}
Welcome.jsx
import {AiFillPlayCircle} from 'react-icons/ai';
import {SiEthereum} from 'react-icons/si';
import {BsInfoCircle} from 'react-icons/bs';
import {Loader} from './';
import { TransactionContext } from '../context/TransactionContext';
import React, {useContext} from 'react';
const commonStyles = "min-h-[70px] sm:px-0 px-2 sm:min-w-[120px] flex justify-center items-center border-[0.5px] border-gray-400 text-sm font-light text-white";
const Input = ({placeholder, name, type, value, handleChange}) => (
<input
placeholder={placeholder}
type={type}
step="0.0001"
value={value}
onChange={(e) => handleChange(e, name)}
className="my-2 w-full rounded-sm p-2 outline-none bg-transparent text-white border-none text-sm white-glassmorphism" />
);
const Welcome = () => {
const {connectWallet, currentAccount, formData, sendTransaction, handleChange} = useContext(TransactionContext);
const handleSubmit = (e) => {
const { addressTo, amount, keyword, message } = formData;
e.preventDefault();
if(!addressTo || !amount || !keyword || !message) return;
sendTransaction();
};
return (
<div className="flex w-full justify-center items-center">
<div className="flex mf:flex-row flex-col items-start justify-between md:p-20 py-12 px-4">
<div className="flex flex-1 justify-start flex-col mf:mr-10 ">
<h1 className="text-3xl sm:text-5xl text-white text-gradient py-1">
Send Crypto <br/> across the world
</h1>
<p className="text-left mt-5 text-white font-light md:w-9/12 w-11/12 text-base">
Explore the universe of crypto trade cryptocurrencies smoothly and securely on Krypto
</p>
{!currentAccount && (<button type="button" onClick={connectWallet}
className="flex flex-row justify-center items-center my-5 bg-[#2952e3] p-3 rounded-full cursor-pointer hover:bg-[2546bd]">
<p className="text-white text-base font-semibold" >Connect Your Wallet </p>
</button>)}
<div className="grid sm:grid-cols-3 grid-cols-2 w-full mt-10">
<div className={`rounded-tl-2xl ${commonStyles}`}>
Reliability
</div>
<div className={commonStyles}>
Security
</div>
<div className={`rounded-tr-2xl ${commonStyles}`}>
Ethereum
</div>
<div className={`rounded-bl-2xl ${commonStyles}`}>
Web 3.0
</div>
<div className={commonStyles}>
Low fees
</div>
<div className={`rounded-br-2xl ${commonStyles}`}>
Blockchain
</div>
</div>
</div >
<div className="flex flex-col flex-1 items-center justify-start w-full mf:mt-0 mt-10">
<div className="p-3 justify-end items-start flex-col rounded-xl h-40 sm:w-72 w-full my-5 eth-card white-glassmorphism">
<div className="flex justify-between flex-col w-full h-full">
<div className="flex justify-between items-start">
<div className="w-10 h-10 rounded-full border-2 border-white flex justify-center items-center">
<SiEthereum fontSize={21} color="#fff"/>
</div>
<BsInfoCircle fontSize={17} color="fff"/>
</div>
<div>
<p className="text-white font-light text-sm ">
Address
</p>
<p className="text-white font-semibold text-lg mt-1 ">
Ethereum
</p>
</div>
</div>
</div>
<div className="p-5 sm:w-96 w-full flex flex-col justify-start items-center blue-glassmorphism">
<Input placeholder="Address to" name="addressTo" type="text" handleChange={handleChange} />
<Input placeholder="Amount of (ETH) " name="amount" type="number" handleChange={handleChange} />
<Input placeholder="Keyword (Gif)" name="Keyword" type="text" handleChange={handleChange} />
<Input placeholder="Enter message" name="message" type="text" handleChange={handleChange} />
<div className ="h-[1px] w-full bg-gray-400 my-2"/>
{false ? (
<Loader/>
) : (
<button
type="button" onClick={handleSubmit}
className="text-white w-full mt-2 border-[1px] p-2 border-[#3d4f7c] rounded-full cursor-pointer">
Send
</button>
)}
</div>
</div>
</div>
</div>
);
}
export default Welcome;
Main.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import './index.css'
import {TransactionProvider} from './context/TransactionContext';
ReactDOM.render(
<TransactionProvider>
<React.StrictMode>
<App />
</React.StrictMode>
</TransactionProvider>,
document.getElementById('root')
)
I am implementing authentication on nextjs strapi graphql and I want to redirect user once he registers or logins or theres is jwt token in cookies but when I login. its not redirecting using router.push method, I am also checking in other pages if user exist in cookies but use`Effect not working Here is my code example
System Information
4.1.11:
Macos:
Mysql:
14.17.00:
6.14.13:
1.22.19:
import { useContext, useEffect, useState } from "react";
import Image from "next/image";
import Link from "next/link";
import { useMutation } from "#apollo/client";
import { LOGIN_USER } from "../../gql/mutations";
import { userContext } from "../../context/wrapppers/UserWrapper";
import { setUserAction } from "../../context/actions/user-actions";
import { SIGN_IN } from "../../context/actions/action-types";
import { useRouter } from "next/router";
function login() {
const router = useRouter();
const { user, dispatchUser } = useContext(userContext);
const [credentials, setCredentials] = useState({});
useEffect(() => {
if (user !== "" && user !== undefined) {
router.push("/account");
}
}, []);
const [loginUser, { loading, error, data }] = useMutation(LOGIN_USER);
useEffect(() => {
if (data) {
dispatchUser(
setUserAction(
{
token: data.login.jwt,
username: data.login.user.username,
id: data.login.user.id,
email: data.login.user.email,
},
SIGN_IN
)
);
}
}, [data]);
if (loading) return <h1>logging in...</h1>;
if (error) return <h1>{error.message}</h1>;
console.log("user from login page", user);
const handleChange = (e) => {
setCredentials({
...credentials,
[e.target.name]: e.target.value,
});
};
const handleLogin = (e) => {
if (typeof window === "undefined") {
return;
}
e.preventDefault();
loginUser({
variables: {
input: credentials,
},
});
};
return (
<div className="h-screen bg-slate-50 flex justify-center items-center w-full">
{error && <div>{error.message}</div>}
<form onSubmit={handleLogin}>
<div className="bg-white px-10 py-8 rounded-xl w-screen shadow-md max-w-sm">
<Image
src={require("../../public/Img/logo.png")}
height={50}
objectFit="contain"
className="h-14 mb-4 mx-auto"
alt=""
/>
<div className="space-y-4">
<h1 className="text-center text-2xl font-semibold text-gray-600">
Login
</h1>
<div>
<label
for="email"
className="block mb-1 text-gray-600 font-semibold"
>
Email or Username
</label>
<input
name="identifier"
onChange={handleChange}
type="text"
required
className="bg-indigo-50 px-4 py-2 outline-none rounded-md w-full"
/>
</div>
<div>
<label
for="password"
className="block mb-1 text-gray-600 font-semibold"
>
Password
</label>
<input
name="password"
onChange={handleChange}
type="text"
required
className="bg-indigo-50 px-4 py-2 outline-none rounded-md w-full"
/>
</div>
</div>
<button
type="submit"
className="mt-4 w-full bg-yellow-500 font-semibold py-2 rounded-md tracking-wide"
>
Login
</button>
<Link href="account/register">
<h3 className="mt-2 cursor-pointer">Or Create an account</h3>
</Link>
</div>
</form>
</div>
);
}
export default login;
function index() {
const router = useRouter();
const { user, dispatchUser } = useContext(userContext);
const [getUserOrders, { loading, error, data }] = useLazyQuery(USER_ORDERS);
useEffect(() => {
if (user) {
getUserOrders({
variables: {
filters: {
user: {
id: {
eq: user.id,
},
},
status: {
ne: "rejected",
},
},
},
});
} else {
router.push("/account/login");
}
}, [user]);
if (error) return <div>{error.message}</div>;
if (loading) return <div>Loading ...</div>;
const handleLogout = () => {
router.push("/account/login");
dispatchUser(setUserAction(user, SIGN_OUT));
};
return (
<div>
<section className="py-5 sm:py-7 bg-gray-100">
<div className="container max-w-screen-xl mx-auto px-4">
Home/Account
</div>
</section>
<section className="py-10">
<div className="container max-w-screen-xl mx-auto px-4">
<div className="flex flex-col md:flex-row -mx-4">
<aside className="md:w-1/3 lg:w-1/4 px-4">
<ul>
<li>
<Link href="/account">
<span className="block px-3 py-2 text-primary bg-gray-100 hover:bg-orange-100 rounded-md">
Account
</span>
</Link>
</li>
<li>
<div
className="cursor-pointer flex items-center"
onClick={handleLogout}
>
<IoIosLogOut size="20" className="mr-2" />
<span>Log out</span>
</div>
</li>
</ul>
</aside>
<main className="md:w-2/3 lg:w-3/4 px-4">
<article className="border border-gray-200 bg-white shadow-sm rounded mb-5 p-3 lg:p-5">
<figure className="flex items-start sm:items-center">
<AiOutlineUser size="50" />
<figcaption>
<h5 className="font-semibold text-lg">
Hi {user.username}
</h5>
<p>Email: {user.email}</p>
</figcaption>
</figure>
<hr className="my-4" />
<h3 className="text-xl font-semibold mb-5">Order History</h3>
{data && data.orders.data.length !== 0 ? (
data.orders.data.map(({ id, attributes }, orderIndex) => (
<article
key={id}
className="p-3 lg:p-5 mb-5 bg-white border border-blue-600 rounded-md"
>
<header className="lg:flex justify-between mb-4">
<div className="mb-4 lg:mb-0">
<p className="font-semibold">
<span>Order ID: #{id} </span>
<span className="text-green-500">
{" "}
• {attributes.status}{" "}
</span>
</p>
<p className="text-gray-500">
{" "}
{new Date(
attributes.createdAt
).toLocaleDateString()}
</p>
</div>
</header>
<hr className="my-4" />
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-2">
{attributes.products.data.map(
({ id, attributes }, productIndex) => (
<Link href={`/shop/${attributes.slug}`}>
<figure
key={id}
className="flex flex-row mb-4 cursor-pointer"
>
<div>
<Image
src={`${
BACKEND_URL +
attributes.media.data[0].attributes.url
}`}
width={80}
height={80}
objectFit="cover"
className="rounded"
/>
</div>
<figcaption className="ml-3">
<p className="text-gray-600">
{attributes.name}
</p>
<p className="mt-1 font-semibold">
{
data.orders.data[orderIndex].attributes
.details[productIndex].quantity
}{" "}
x ${attributes.price}
</p>
</figcaption>
</figure>
</Link>
)
)}
</div>
</article>
))
) : (
<EmptyState
title="You have not yet ordered"
subtitle="Choose some products to order"
image={EmptyOrder}
btnText="Go to shop"
btnText2="Go Home"
btnLink="/shop"
btnLink2="/"
/>
)}
</article>
</main>
</div>
</div>
</section>
</div>
);
}
export default index;
I am quite new in nextjs and graphql any suggesstion highly appreciated!
I am trying to implement reCAPTCHA using the react-hook-form in combination with react-hook-recaptcha Not sure what is the reason but I am getting the following error for window saying it's undefined:
ReferenceError: window is not defined
> 33 | const { recaptchaLoaded, recaptchaWidget } = useRecaptcha({
Code:
import React, { useState } from "react";
import { useRecaptcha } from "react-hook-recaptcha";
import { useForm, SubmitHandler } from "react-hook-form";
import { urlFor } from "#lib/sanity";
import { dateFormat } from "#lib/helpers";
import { PortableText } from "#portabletext/react";
import { portableTextComponents } from "#components/portable-text";
import { FormError, FormSuccess } from "#components/Form";
import { ArticleProps, Comment } from "types/article";
const sitekey = "6Ld-*********"; // change to your site key
const containerId = "recaptcha"; // this id can be customized
declare global {
interface Window {
grecaptcha: any;
}
}
const ArticleSingle = ({ page }: ArticleProps) => {
const [submitted, setSubmitted] = useState(false);
const { _id, body, featuredImage, title, author, publishedAt, comments } =
page;
const successCallback = (response: any) => {
console.log("YUP");
};
const { recaptchaLoaded, recaptchaWidget } = useRecaptcha({
containerId,
successCallback,
sitekey,
size: "invisible",
});
const executeCaptcha = () => {
if (recaptchaWidget !== null) {
window.grecaptcha.reset(recaptchaWidget);
window.grecaptcha.execute(recaptchaWidget);
}
};
const {
register,
handleSubmit,
formState: { errors },
} = useForm<Comment>();
const onSubmit: SubmitHandler<Comment> = async (e, data) => {
e.preventDefault();
executeCaptcha();
fetch("/api/create-comment", {
method: "POST",
body: JSON.stringify(data),
})
.then(() => {
console.log(data);
setSubmitted(true);
})
.catch((error) => {
console.log(error);
setSubmitted(false);
});
};
return (
<div id="article">
<div className="relative mb-4 md:mb-0" style={{ height: "45rem" }}>
<div
className="absolute bottom-0 left-0 z-10 w-full h-full"
style={{
backgroundImage:
"linear-gradient(180deg,transparent,rgba(0,0,0,.75))",
}}
></div>
{featuredImage && featuredImage.asset && (
<img
alt={featuredImage?.alt || ""}
src={urlFor(featuredImage).quality(85).url()}
className="absolute top-0 left-0 z-0 object-cover w-full h-full"
/>
)}
<div className="absolute left-0 right-0 z-20 max-w-screen-lg p-4 mx-auto bottom-2">
{title && (
<h1 className="text-4xl font-semibold leading-tight text-gray-100 md:text-5xl lg:text-7xl">
{title}
</h1>
)}
<div className="flex gap-5 mt-5 place-items-center">
{author?.featuredImage && (
<img
className="object-cover object-center w-12 h-12 border-2 border-white rounded-full shadow-lg md:w-16 md:h-16"
alt={author?.featuredImage?.alt || ""}
src={urlFor(author.featuredImage).quality(85).url()!}
/>
)}
<div>
{author && (
<p className="font-semibold text-gray-200 sm:text-md md:text-xl">
{author.name}
</p>
)}
{publishedAt && (
<time className="font-semibold text-bubblegum sm:text-md md:text-xl">
{dateFormat(publishedAt)}
</time>
)}
</div>
</div>
</div>
</div>
<div className="max-w-screen-lg px-4 pb-8 mx-auto mt-12 text-navyBlue text-md md:text-xl md:leading-relaxed">
<PortableText value={body} components={portableTextComponents} />
{/* Comment Form */}
<div id="article-comments">
<hr className="my-5 mb-10 border border-yellow-500" />
{submitted ? (
<FormSuccess
title="Thank you for submitting your comment!"
message="Once it has been approved, it will appear below!"
/>
) : (
<>
<h3 className="text-md text-bubblegum">Enjoyed this article?</h3>
<h4 className="text-3xl font-bold">Leave a comment below!</h4>
<hr className="py-3 mt-2" />
<form
className="flex flex-col pb-5 mx-auto"
onSubmit={handleSubmit(onSubmit)}
>
<input
{...register("_id")}
type="hidden"
name="_id"
value={_id}
/>
<input
{...register("name", {
required: "The Name Field is required",
})}
className="block w-full px-3 py-2 mt-4 border rounded shadow outline-none form-input ring-bubblegum focus:ring"
placeholder="Name"
type="text"
/>
{errors.name && <FormError message={errors.name.message} />}
<input
{...register("email", {
required: "The Email Field is required",
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
message: "Enter a valid e-mail address",
},
})}
className="block w-full px-3 py-2 mt-4 border rounded shadow outline-none form-input ring-bubblegum focus:ring"
placeholder="Email"
type="email"
/>
{errors.email && <FormError message={errors.email.message} />}
<textarea
{...register("comment", {
required: "The Comment Field is required",
minLength: {
value: 40,
message: "A Min of 40 characters is required",
},
})}
className="block w-full px-3 py-2 mt-4 border rounded shadow outline-none resize-none form-textarea ring-bubblegum focus:ring"
placeholder="Comment"
rows={7}
/>
{errors.comment && (
<FormError message={errors.comment.message} />
)}
<input
disabled={!recaptchaLoaded}
type="submit"
className="flex-none w-32 px-4 py-2 mt-6 text-xs font-bold text-center uppercase duration-300 ease-in-out bg-transparent border-2 rounded-full cursor-pointer text-navyBlue border-navyBlue hover:bg-navyBlue hover:text-bubblegum"
></input>
</form>
{/* Comments */}
{comments.length > 0 && (
<div className="flex flex-col my-10">
<h3 className="text-4xl">Comments</h3>
<hr className="mt-2 mb-5 border-2 border-yellow-500" />
{comments.map(({ name, _id, _createdAt, comment }) => (
<div
className="px-4 py-6 bg-white rounded-sm shadow-md"
key={_id}
>
<p>
<time className="block text-sm font-bold">
{dateFormat(_createdAt)}{" "}
</time>
<span className="text-bubblegum">{name}</span> :
<span className="pl-2">{comment}</span>
</p>
</div>
))}
</div>
)}
</>
)}
</div>
</div>
</div>
);
};
export default ArticleSingle;
Next.js has a server-side environment, and window object only exists in the browser. You can check which environment code is running with this:
const isServerSide = typeof window === 'undefined'
So your code will probably look like:
const executeCaptcha = () => {
if (recaptchaWidget !== null && typeof window !== 'undefined') {
window.grecaptcha.reset(recaptchaWidget);
window.grecaptcha.execute(recaptchaWidget);
}
};