reactjs remember me functionality on localstorage - reactjs

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

Related

How to solve the error: Cannot update a component (`Component`) while rendering a different component (`otherComponent`)

I'm building a dynamic Alert UI with tailwindcss. I want to make this alert so that it can be used anywhere when making all requests. I also combine the Alert with the value in the global state. Overall it works well, however there are the following caveats:
this the global state of alert used redux toolki:
import { createSlice } from '#reduxjs/toolkit';
const uiSlice = createSlice({
name: 'UI',
initialState: {
offcanvasVisible: false,
alert: {
isShow: false,
variant: '',
message: '',
},
},
reducers: {
offcanvasToggle: (state) => {
state.offcanvasVisible = !state.offcanvasVisible;
},
showAlert: (state, action) => {
state.alert.isShow = true;
state.alert.message = action.payload.message;
state.alert.variant = action.payload.variant;
},
closeAlert: (state) => {
state.alert.isShow = false;
state.alert.message = '';
state.alert.variant = '';
},
},
});
export const { offcanvasToggle, showAlert, closeAlert } = uiSlice.actions;
export default uiSlice.reducer;
the following code in the Alert component:
import { BiNoEntry } from 'react-icons/bi';
import { IoCloseSharp } from 'react-icons/io5';
import { BsCheckCircleFill, BsFillInfoCircleFill } from 'react-icons/bs';
import { useDispatch, useSelector } from 'react-redux';
import { closeAlert } from '../../store/ui-slice';
import { useEffect, useState } from 'react';
const Alert = () => {
const [showState, setShowState] = useState(false);
const [variantState, setVariantState] = useState('');
const [messageState, setMessageState] = useState('');
const [classes, setClasses] = useState('');
const dispatch = useDispatch();
const { variant, message, isShow } = useSelector((state) => state.ui.alert);
useEffect(() => {
if (!isShow) return;
setShowState(isShow);
setVariantState(variant);
setMessageState(message);
if (variant === 'info') {
setClasses('bg-blue-400 border-blue-500');
}
if (variant === 'success') {
setClasses('bg-green-400 border-green-500');
}
if (variant === 'failed') {
setClasses('bg-red-400 border-red-500');
}
}, [isShow, variant, message]);
return (
<div
className={`${
!showState && 'hidden'
} container-custom w-full py-4 text-white flex flex-row gap-2 items-center justify-between ${classes}`}
>
<div className="flex flex-row gap-2 items-center">
<span>
{variantState === 'info' ? (
<BsFillInfoCircleFill size="24" />
) : variantState === 'success' ? (
<BsCheckCircleFill size="24" />
) : variantState === 'failed' ? (
<BiNoEntry size="24" />
) : (
''
)}
</span>
{messageState}
</div>
<button
onClick={() => {
dispatch(closeAlert());
}}
className="text-white"
>
<IoCloseSharp color="white" size="24" />
</button>
</div>
);
};
export default Alert;
Code in the Contact Component:
/* eslint-disable jsx-a11y/iframe-has-title */
import LandingLayout from '../../components/Layout/LandingLayout';
import Breadcrumb from '../../components/UI/Breadcrumb';
import Input from '../../components/Form/Input';
import TextArea from '../../components/Form/TextArea';
import { AiOutlineWhatsApp } from 'react-icons/ai';
import { BsInstagram } from 'react-icons/bs';
import {
MdOutlineAddLocationAlt,
MdOutlineEmail,
MdPhone,
} from 'react-icons/md';
import Button from '../../components/UI/Button';
import { useForm } from 'react-hook-form';
import { useDispatch, useSelector } from 'react-redux';
import {
sendingEmail,
turnOffLoadingSendEmail,
turnOnLoadingSendEmail,
} from '../../store/landing-slice';
import Spin from '../../components/UI/Spin';
import { showAlert } from '../../store/ui-slice';
const Kontak = () => {
const dispatch = useDispatch();
const { loading, success, error } = useSelector(
(state) => state.landing.sendEmail
);
const {
register,
handleSubmit,
formState: { errors, isValid },
reset,
} = useForm({
mode: 'all',
});
if (success !== null) {
dispatch(showAlert({ variant: 'success', message: success.message }));
}
const onSubmit = (data) => {
if (!isValid) return;
dispatch(turnOnLoadingSendEmail());
setTimeout(() => {
dispatch(sendingEmail(data));
dispatch(turnOffLoadingSendEmail());
reset();
}, 1000);
};
return (
<LandingLayout>
<Breadcrumb title="Kontak Kami" />
<section className="container-custom py-10 w-full h-[450px] overflow-hidden">
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3960.687915874803!2d107.64700641530236!3d-6.927857094994467!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x2e68e80a699f1971%3A0xca4c51951a56650c!2sPanti%20Asuhan%20Al-Hidayah!5e0!3m2!1sid!2sid!4v1666696368586!5m2!1sid!2sid"
width="100%"
height="100%"
style={{ border: 0 }}
allowFullScreen=""
loading="lazy"
referrerPolicy="no-referrer-when-downgrade"
/>
</section>
<section className="container-custom py-4 w-full flex flex-col gap-10 md:flex-row-reverse">
<form onSubmit={handleSubmit(onSubmit)} className="w-full">
<h2 className="text-gray-700 text-lg mb-3">
Hubungi kami melalui form dibawah ini.
</h2>
<div className="flex flex-col lg:flex-row lg:gap-3">
<Input
options={{
...register('name', {
required: 'Nama Lengkap tidak boleh kosong',
}),
}}
id="name"
label="Nama Lengkap"
requireIcon="true"
hasError={!!errors?.name}
errorMessage={errors?.name?.message}
/>
<Input
options={{
...register('email', {
required: 'E-Mail tidak boleh kosong',
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address',
},
}),
}}
id="email"
label="E-Mail"
requireIcon="true"
hasError={!!errors?.email}
errorMessage={errors?.email?.message}
/>
</div>
<Input
options={{
...register('subject', { required: 'Subjek tidak boleh kosong' }),
}}
id="subject"
label="Subjek"
requireIcon="true"
hasError={!!errors?.subject}
errorMessage={errors?.subject?.message}
/>
<TextArea
id="keterangan"
label="Keterangan"
options={{
...register('keterangan'),
rows: '4',
}}
></TextArea>
<Button
className="flex gap-2"
options={{
type: 'submit',
disabled: !isValid,
}}
>
{loading && <Spin />}
Kirim
</Button>
</form>
<div className="w-full flex flex-col gap-2 md:gap-4">
<div className="flex flex-row gap-2">
<span className="self-start rounded-full bg-gray-300 text-gray-700 p-3">
<MdOutlineAddLocationAlt size={32} />
</span>
<div className="flex flex-col gap-1">
<h3 className="pt-5 font-semibold text-[17px]">Alamat</h3>
Jl. Trs. St. Kiaracondong, RT.02/RW.08, Kebun Jayanti, Kec.
Kiaracondong, Kota Bandung, Jawa Barat 40281
</div>
</div>
<div className="flex flex-row gap-2">
<span className="self-start rounded-full bg-gray-300 text-gray-700 p-3">
<MdPhone size={32} />
</span>
<div className="flex flex-col gap-1">
<h3 className="pt-5 font-semibold text-[17px]">Telepon</h3>
022 7333116
</div>
</div>
<div className="flex flex-row gap-2">
<span className="self-start rounded-full bg-gray-300 text-gray-700 p-3">
<AiOutlineWhatsApp size={32} />
</span>
<div className="flex flex-col gap-1">
<h3 className="pt-5 font-semibold text-[17px]">Whatsapp</h3>
0882 43556 7721
</div>
</div>
<div className="flex flex-row gap-2">
<span className="self-start rounded-full bg-gray-300 text-gray-700 p-3">
<BsInstagram size={32} />
</span>
<div className="flex flex-col gap-1">
<h3 className="pt-5 font-semibold text-[17px]">Instagram</h3>
yayasan_alhidayah
</div>
</div>
<div className="flex flex-row gap-2">
<span className="self-start rounded-full bg-gray-300 text-gray-700 p-3">
<MdOutlineEmail size={32} />
</span>
<div className="flex flex-col gap-1">
<h3 className="pt-5 font-semibold text-[17px]">E-Mail</h3>
alhidayahkircon#gmail.com
</div>
</div>
</div>
</section>
</LandingLayout>
);
};
export default Kontak;
I put the Alert on the Navbar:
import { Link } from 'react-router-dom';
import Logo from '../UI/Logo';
import { FiMenu, FiPhone } from 'react-icons/fi';
import Button from '../UI/Button';
import Dropdown from '../UI/Dropdown';
import { BiDonateHeart } from 'react-icons/bi';
import { AiOutlineHistory } from 'react-icons/ai';
import Alert from '../UI/Alert';
const Navbar = (props) => {
return (
<header className="sticky top-0 z-10 bg-white border-b-2 border-slate-200">
<Alert />
<div className="container-custom flex justify-between items-center">
<div className="w-36 pl-2 md:pl-0">
<Link to="/">
<Logo />
</Link>
</div>
<ul className="hidden lg:flex font-medium">
<li className="mr-1 hover:bg-gray-100 py-2 px-4 rounded-md">
<Link to={'/'}>Beranda</Link>
</li>
<li className="mr-1 hover:bg-gray-100 py-2 px-4 rounded-md">
<Link to={'/kegiatan'}>Kegiatan</Link>
</li>
<li className="mr-1 hover:bg-gray-100 py-2 px-4 rounded-md">
<Dropdown label="Donasi">
<li className="block px-4 py-2 hover:bg-gray-100">
<Link className="flex items-center" to="/donasi">
<span className="mr-3">
<BiDonateHeart />
</span>
Formulir Donasi
</Link>
</li>
<li className="block px-4 py-2 hover:bg-gray-100">
<Link className="flex items-center" to="/cek-donasi">
<span className="mr-3">
<AiOutlineHistory />
</span>
<p>Cek Donasi</p>
</Link>
</li>
</Dropdown>
</li>
<li className="mr-1 hover:bg-gray-100 py-2 px-4 rounded-md">
<Dropdown label="Tentang Kami">
<li className="block px-4 py-2 hover:bg-gray-100">
<Link to="/profil-lembaga">Profil Lembaga</Link>
</li>
<li className="block px-4 py-2 hover:bg-gray-100">
<Link to="/visi-misi">Visi & Misi</Link>
</li>
<li className="block px-4 py-2 hover:bg-gray-100">
<Link to="/galeri">
<p>Galeri</p>
</Link>
</li>
<li className="block px-4 py-2 hover:bg-gray-100">
<Link to="/kontak">Hubungi Kami</Link>
</li>
</Dropdown>
</li>
</ul>
<div className="hidden lg:grid grid-cols-2 gap-1">
<a
className="flex items-center mr-1 hover:bg-gray-100 py-2 px-4 rounded-md"
href="tel:+0222334645"
>
<FiPhone size={'24'} />
<span className="ml-2">022 2334645</span>
</a>
<Button
className="flex justify-center items-center"
options={{
type: 'link',
href: '/donasi',
}}
>
Donasi
<span className="ml-1">
<BiDonateHeart />
</span>
</Button>
</div>
<div className="lg:hidden" onClick={props.offcanvasToggle}>
<FiMenu size={32} />
</div>
</div>
</header>
);
};
export default Navbar;
and here's what the UI should look like:
First of all, I thank you for being petrified
I hope I can use the best way to implement Alert without Warning like this.
This occurs because your updates to state are cascading. Your first set is causing the DOM to update. The update isn't yet complete, so your second (or third) set is also trying to update the DOM, and causing conflict.
I have found that when I have situations where I'm updating multiple state variables simultaneously it is better to use useReducer instead of useState. You can then setup a dispatch method that updates your multiple bits of state simultaneously, and even provide multiple methods that do different updates conditionally. This way state updates are concurrent, and avoid the errors you are seeing.

Nextjs redirecting and updating changes not working in strapi apollo graphql authentication

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!

Match Params Id not working, how to fix it?

So i'm trying to access the id of my ypdate, and the example code i fould uses Match, I don't think the newer version of React likes it. I think I need to implement useParams(), however I new to coding and i'm not sure how to. Here is my code with Match params
import { useEffect } from "react";
import { Redirect } from "react-router-dom";
import { PlusCircleIcon, BookOpenIcon } from "#heroicons/react/solid";
import { useFormik } from "formik";
import * as Yup from "yup";
import { useDispatch, useSelector } from "react-redux";
import {
fetchCategoryAction,
updateCategoriesAction,
deleteCategoriesAction,
} from "../../redux/slices/category/categorySlice";
//Form schema
const formSchema = Yup.object({
title: Yup.string().required("Title is required"),
});
const UpdateCategory = ({
match: {
params: { id },
},
}) => {
const dispatch = useDispatch();
//fetch single category
useEffect(() => {
dispatch(fetchCategoryAction(id));
}, []);
//get data from store
const state = useSelector(state => state?.category);
const { loading, appErr, serverErr, category, isEdited, isDeleted } = state;
//formik
const formik = useFormik({
enableReinitialize: true,
initialValues: {
title: category?.title,
},
onSubmit: values => {
//build up the date for update
//dispath the action
dispatch(updateCategoriesAction({ title: values.title, id }));
},
validationSchema: formSchema,
});
//redirect
if (isEdited || isDeleted) return <Redirect to="/category-list" />;
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8">
<div>
<BookOpenIcon className="mx-auto h-12 w-auto" />
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
Update Category
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
<p className="font-medium text-indigo-600 hover:text-indigo-500">
These are the categories user will select when creating a post
</p>
{/* Display err */}
<div>
{appErr || serverErr ? (
<h2 className="text-red-500 text-center text-lg">
{serverErr} {appErr}
</h2>
) : null}
</div>
</p>
</div>
{/* Form */}
<form onSubmit={formik.handleSubmit} className="mt-8 space-y-6">
<input type="hidden" name="remember" defaultValue="true" />
<div className="rounded-md shadow-sm -space-y-px">
<div>
<label htmlFor="email-address" className="sr-only">
Name
</label>
{/* Title */}
<input
value={formik.values.title}
onChange={formik.handleChange("title")}
onBlur={formik.handleBlur("title")}
type="text"
autoComplete="text"
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 text-center focus:z-10 sm:text-sm"
placeholder="New Category"
/>
<div className="text-red-400 mb-2">
{formik.touched.title && formik.errors.title}
</div>
</div>
</div>
<div>
<div>
{/* Submit */}
{loading ? (
<button
disabled
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-gray-600 "
>
<span className="absolute left-0 inset-y-0 flex items-center pl-3">
<PlusCircleIcon
className="h-5 w-5 text-yellow-500 group-hover:text-indigo-400"
aria-hidden="true"
/>
</span>
Loading please wait...
</button>
) : (
<>
<button
type="submit"
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-yellow-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
<span className="absolute left-0 inset-y-0 flex items-center pl-3">
<PlusCircleIcon
className="h-5 w-5 text-yellow-500 group-hover:text-indigo-400"
aria-hidden="true"
/>
</span>
Update Category
</button>
<button
onClick={() => dispatch(deleteCategoriesAction(id))}
type="submit"
className="group mt-2 relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Delete Category
</button>
</>
)}
</div>
</div>
</form>
</div>
</div>
);
};
export default UpdateCategory;
Nothing renders to the screen with it in there I take it out and I do, however the functionality i need for updating isn't there. Any help would be greatly appreciated.

Next.js form reCAPTCHA returns window of undefined - react-hook-recaptcha

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);
}
};

Import password filed as an functional component in ReactJs

I want to store this react hook and password field in a separate js file and I want to import it to my register form how can I do that?
Password Field
<label class="font-medium block mb-1 mt-6 text-gray-700" for="password">
Password
</label>
<div class="relative w-full">
<div class="absolute inset-y-0 right-0 flex items-center px-2">
<input class="hidden js-password-toggle" id="toggle" type="checkbox" />
<label class="bg-gray-300 hover:bg-gray-400 rounded px-2 py-1 text-sm text-gray-600 font-mono cursor-pointer js-password-label" for="toggle">show</label>
</div>
<input class="appearance-none border-2 rounded w-full py-3 px-3 leading-tight border-gray-300 bg-gray-100 focus:outline-none focus:border-indigo-700 focus:bg-white text-gray-700 pr-16 font-mono js-password" id="password" type="password" autocomplete="off"
/>
</div>
Password function filed
useEffect(() => {
const passwordToggle = document.querySelector('.js-password-toggle')
passwordToggle.addEventListener('change', function() {
const password = document.querySelector('.js-password'),
passwordLabel = document.querySelector('.js-password-label')
if (password.type === 'password') {
password.type = 'text'
passwordLabel.innerHTML = 'hide'
} else {
password.type = 'password'
passwordLabel.innerHTML = 'show'
}
password.focus()
})
}, [])
Form That I want to add this password filed
import React, { useEffect, useState } from "react";
import Buttons from "../../../elements/form/Button";
import PasswordInput from "../../../elements/form/PasswordInput";
import { Col, Input, Row, Select, Form, InputNumber, Divider } from "antd";
import { ChangePassword, sentOtp, changePhoneNumber, requestOtp } from "./SecurityApi";
const { Option } = Select;
const Security = () => {
const [phoneNo, setPhoneNo] = useState("");
const [otp, setOtp] = useState("");
//const [password, setPassword] = useState("");
const [conPassword, setConPassword] = useState("");
const [passError, setPassError] = useState();
const [otpError, setOtpError] = useState();
const [user, setUser] = useState(null);
//changing password
const [editPasswordState, setEditPasswordState] = useState(false);
const [password, setPassword] = useState({
password: "",
});
const handlePasswordChange = async () => {
if (password === user.password) {
return;
}
if (password.trim() === "") {
console.log("cant be empty");
return;
}
await ChangePassword(password, setUser);
};
useEffect(() => {
setPassError(password && conPassword && password !== conPassword);
}, [password, conPassword]);
useEffect(() => {
setOtpError(!otp && phoneNo);
}, [otp]);
//onSubmit password
const onSubmit = async () => {
if (passError) return false;
try {
const res = await ChangePassword({ password });
setPassword("");
setConPassword("");
alert("Password Changed !!");
} catch (error) {
alert("Something went wrong !!");
}
};
//OTP
const sendOtp = async () => {
if (phoneNo === "") return false;
try {
const res = await sentOtp(phoneNo);
alert(`Otp is ${res} !!`);
} catch (error) {
alert("Something went wrong !!");
}
};
//Changing the phone number
const changePhoneNo = async () => {
if (phoneNumber === "") return false;
if (otp === "") {
setOtpError(true);
return false;
}
try {
const res = await changePhoneNumber(phoneNo);
setPhoneNumber("");
setOtp("");
alert("Phone Number Changed !!");
} catch (error) {
alert("Something went wrong !!");
}
};
const [editPhoneNumberState, setEditPhoneNumberState] = useState(false);
const [phoneNumber, setPhoneNumber] = useState({
password: "",
});
const handlePhoneNumberChange = async () => {
if (phoneNumber === user.phoneNumber) {
return;
}
if (password.trim() === "") {
console.log("cant be empty");
return;
}
await ChangePassword(password, setUser);
};
return (
<>
<div className="md:pl-8 sm:pl-0 ">
{/* ---------- Password Change ------------ */}
<div class="mt-10 sm:mt-0">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<div class="px-4 sm:px-0">
<h3 class="text-lg font-medium leading-6 text-gray-900">
Change Password
</h3>
<p class="mt-1 text-sm text-gray-600">Change password here</p>
</div>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<form action="#" method="POST">
<div class="shadow overflow-hidden sm:rounded-md">
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<Buttons
title={
!editPasswordState ? "Edit Full Password" : "Cancel"
}
outline
onClick={() => {
setEditPasswordState(!editPasswordState);
}}
class="inline-flex
justify-center
py-2 px-4 border
border-transparent
shadow-sm text-sm
font-medium rounded-md
text-white bg-red-400
hover:bg-red-400
focus:outline-none f
ocus:ring-2
focus:ring-offset-2
focus:ring-red-500"
/>
</div>
<div class="col-span-6 sm:col-span-3">
{editPasswordState && (
<Buttons
title="Save"
onClick={async () => {
setEditPasswordState(false);
await handlePasswordChange();
}}
/>
)}
</div>
</div>
</div>
<div class="px-4 py-5 bg-white sm:p-6">
{/* OTP Check*/}
<div class="grid grid-cols-6 gap-1">
<div class="col-span-4 sm:col-span-3">
<label
for="first-name"
class="block text-sm font-medium text-gray-700"
>
Send OTP
</label>
<Buttons
title={"Request OTP"}
outline
onClick={async () => {
await requestOtp(user.phoneNumber);
}}
/>
</div>
<div class="col-span-6 sm:col-span-3">
<label
for="last-name"
class="block text-sm font-medium text-gray-700"
>
Enter OTP
</label>
<input
type="text"
name="last-name"
id="last-name"
autocomplete="family-name"
class="mt-1
focus:ring-red-400
focus:border-red-400
block w-full
shadow-sm sm:text-sm
border-gray-300 rounded-md"
/>
{passError && (
<Col className="text-red-500">
OTP doesn't Correct !!
</Col>
)}
</div>
</div>
{/* OTP Check End */}
</div>
<div class="px-4 py-5 bg-white sm:p-6">
{/* Password Check*/}
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label
for="first-name"
class="block text-sm font-medium text-gray-700"
>
New Password
</label>
<Input.Password
value={password}
onChange={(event) => {
setPassword(event.target.value);
}}
class="mt-1
focus:ring-red-400
focus:border-red-400
block w-full
shadow-sm sm:text-sm
border-gray-300 rounded-md"/>
This is the Place
</div>
<div class="col-span-6 sm:col-span-3">
<label
for="last-name"
class="block text-sm font-medium text-gray-700"
>
Confirm New Password
</label>
<Input.Password
value={conPassword}
onChange={(event) => {
setConPassword(event.target.value);
}}
/>
{passError && (
<Col className="text-red-500">
Password doesn't match !!
</Col>
)}
</div>
</div>
{/* Password Check End*/}
</div>
</div>
</form>
</div>
</div>
</div>
{/* ------- End Password Change -------- */}
<div class="px-4 py-1 bg-black-50 text-right sm:px-6">
<Divider></Divider>
</div>
{/* ------------ Mobile Number Change ---------------- */}
<div class="mt-10 sm:mt-0">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<div class="px-4 sm:px-0">
<h3 class="text-lg font-medium leading-6 text-gray-900">
Change Mobile Number
</h3>
<p class="mt-1 text-sm text-gray-600">
Use a permanent address where you can receive mail.
</p>
</div>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<form action="#" method="POST">
<div class="shadow overflow-hidden sm:rounded-md">
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<Buttons
title={
!editPhoneNumberState ? "Edit Phone Number" : "Cancel"
}
outline
onClick={() => {
setEditPhoneNumberState(!editPhoneNumberState);
}}
className={" flex items-center justify-center "}
/>
</div>
<div class="col-span-6 sm:col-span-3">
{editPhoneNumberState && (
<Buttons
title="Save"
onClick={async () => {
setEditPhoneNumberState(false);
await handlePhoneNumberChange();
}}
/>
)}
</div>
</div>
</div>
<div/>
<div class="px-4 py-5 bg-white sm:p-6">
{/** Email Verification */}
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label
for="first-name"
class="block text-sm font-medium text-gray-700"
>
Email
</label>
<Buttons
title="Send Email"
onClick={sendOtp}
outline
class=" inline-flex
justify-center
py-2 px-4 border
border-transparent
shadow-sm text-sm
font-medium rounded-md
text-white bg-red-400
hover:bg-red-400
focus:outline-none f
ocus:ring-2
focus:ring-offset-2
focus:ring-red-500"
/>
</div>
<div class="col-span-6 sm:col-span-3">
<label
for="last-name"
class="block text-sm font-medium text-gray-700"
>
Verification Code
</label>
<input
type="text"
name="last-name"
id="last-name"
autocomplete="family-name"
class="mt-1
focus:ring-red-400
focus:border-red-400
block w-full
shadow-sm sm:text-sm
border-gray-300 rounded-md"
/>
</div>
</div>
{/** End Email Verification */}
{/** Phone Number change */}
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label
for="first-name"
class="block text-sm font-medium text-gray-700"
>
New Mobile Number
</label>
<input
type="text"
name="first-name"
id="first-name"
autocomplete="given-name"
class="mt-1
focus:ring-red-400
focus:border-red-400
block w-full
shadow-sm sm:text-sm
border-gray-300 rounded-md"
/>
</div>
</div>
{/** End Email change */}
</div>
</div>
</form>
</div>
</div>
</div>
{/* ------------ Mobile Number Change ---------------- */}
</div>
</>
);
};
export default Security;
Above mention between two section, the password field should be added
Ok, lets assume this is your form:
import PasswordField from "/PasswordField"
const Form = () => {
const [password, setPassword] = useState("")
const [name, setName] = useState("")
const [lastName, setLastName] = useState("")
const submitForm = () => {
//your logic for submitting form
}
return (
<form onSubmit={submitForm}>
<input type="text" value={name} onChange={(e) => setName(e.target.value)} />
<input type="text" value={lastName} onChange={(e) => setLastName(e.target.value)} />
<PasswordField password={password} setPassword={setPassword} />
</form>
);
};
export default Form;
Note we are importing the PasswordField component to be used inside the form, and passing the value and the function to change that value as props to the PasswordField component.
Now lets create the Password component:
import React, {useState} from 'react';
const PasswordField = (props) => {
return (
<div>
<label class="font-medium block mb-1 mt-6 text-gray-700" for="password">
Password
</label>
<input id={"password"} type="password" value={props.password} onChange={(e) => props.setPassword(e.target.value)} />
</div>
);
};
export default PasswordField;
Obviously, you need to add your classes and styling accordingly, but I thing this could get you going.

Resources