Reset select value depending on siblings state in react-hook-form - reactjs

I have two siblings and one is watching the state of another.
<Controller
control={control}
name="selectedBirthYear"
defaultValue={years[0]}
render={({ field }) => (
<SelectBirthYear
field={field}
years={years}
value={selectedYear}
defaultValue={selectedYear}
onChange={useEffect(() => {setSelectedYear(field.value)})}
/>
)}
/>
and
<Controller
control={control}
name="selectedBirthMonth"
defaultValue={months[0]}
render={({ field }) => (
<SelectBirthMonth
field={field}
startYear={startYear}
selectedYear={selectedYear}
months={months}
value={selectedMonth}
defaultValue={selectedMonth}
reducedMonths={reducedMonths}
onChange={useEffect(() => setSelectedMonth(field.value))}
/>
)}
/>
These two components are part of a Form parent. But, the issue is, that my useEffect, which watches year change, is not firing in combination with the Controller:
const watchYearChange = () => {
if(Number(selectedYear.name) == startYear){
setSelectedMonth(reducedMonths[reducedMonths.length - 1]);
}
};
useEffect(() => watchYearChange(), [selectedYear])
So, I was thinking to put this logic inside of the component itself and 'reset' the value of month on year change (Number(selectedYear.name) == startYear)
I've been experimenting, but for now with no success.
export const SelectBirthMonth = ({
startYear,
selectedYear,
months,
field,
selectedMonth,
setSelectedMonth,
reducedMonths}) => {
const { onChange, value } = field;
const watchYearChange = () => {
if(Number(selectedYear.name) == startYear){
setSelectedMonth(reducedMonths[reducedMonths.length - 1]);
}
console.log(reducedMonths[reducedMonths.length - 1]);
};
useEffect(() => watchYearChange(), [selectedYear])
return (
<Listbox value={Number(selectedYear.name) == startYear ? value == reducedMonths[reducedMonths.length - 1] : value} onChange={onChange}>
{({ open }) => (
<>
<Listbox.Label className="block text-sm font-medium text-gray-700">Geburtsmonat</Listbox.Label>
<div className="mt-1 relative">
<Listbox.Button className="bg-white relative w-full border border-gray-300 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-yellow-500 focus:border-yellow-500 sm:text-sm">
<span className="block truncate">{value?.name}</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<SelectorIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</span>
</Listbox.Button>
<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
static
className="absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
>
{ Number(selectedYear.name) == startYear ? reducedMonths.map((month) => (
<ListboxOption
key={month.id}
value={month}
date={month.name}
>
</ListboxOption>
)) : months.map((month) => (
<ListboxOption
key={month.id}
value={month}
date={month.name}
>
</ListboxOption>
))
}
</Listbox.Options>
</Transition>
</div>
</>
)}
</Listbox>
)
}
Any ideas, how to 'reset' value, which is already coming from field of react hook form? I am really stuck.

Related

How do I use a Navbar to search through Data from in Array and match the search in In Nextjs TypeScript?

type Props = {
topic?: string;
}
function Header({topic}: Props) {
const [istopic, setTopic] = useState("");
const{loading, data, error} = !topic? useQuery(GET_POSTS): useQuery(GET_POSTS_BY_TOPIC,{
variables:{
topic:topic,
}
});
const posts: Post[\] = !topic ? data?.getPostList : data?.getPostListByTopic
//HERE IS THE METHOD I USED TO CREATE AN ARRAY FROM THE QUERY LIST
//HERE IS THE FORM INSIDE OF MY NAVBAR THAT I WANT TO USE TO SEARCH THE RESULTS
<form
onSubmit={handleSubmit}
className="flex flex-1 items-center basis-1/3 space-x-2 rounded-md border-none border-gray-500 px-3 py-1"
>
<input
value={istopic}
onChange={(e) => setTopic(e.target.value)}
type="text"
placeholder="Search"
onSubmit={handleSubmit}
className="flex-1 rounded-md border-gray-400 bg-transparent outline-none "
/>
<SearchIcon className="h-7 w-7 text-gray-400" />
<button disabled={!istopic} type="submit" hidden />
</form>;
{
posts ? (
posts
?.filter((post: any) => post.title.match(new RegExp(istopic, "i")))
?.map((post: any) => {
<div>
<a
href={`/channel/${post.channels[0]?.topic}`}
className=" absolute top-6 z-99 bg-blue-600 block py-2 pl-3 pr-4 text-gray-700 rounded"
aria-current="page"
>
{post.title}
</a>
</div>;
console.log(post.title);
})
) : (
<li>
<a
href="#"
className=" absolute top-6 z-99 bg-blue-600 block py-2 pl-3 pr-4 text-gray-700 rounded hover:bg-gray-100"
aria-current="page"
>
Nothing Found
</a>
</li>
);
}
I want to use the data from the array the to check if there is any that matches from the input field and display those matches

How to set default selected option in combobox headless-ui?

I want the first option to be selected by default in dropdown. I tried combobox defaultValue property but didn't work. How can i do this?
Combobox Component
import { useState } from 'react'
import { CheckIcon, ChevronUpDownIcon } from '#heroicons/react/20/solid'
import { Combobox } from '#headlessui/react'
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export default function FormCombobox({
comboboxData,
label,
questionRange,
setQuestionRange,
}) {
const [query, setQuery] = useState('')
let items = comboboxData.map((item) => ({
id: item.content_object.nanoid,
name: item.content_object.name,
multiplier: item.multiplier,
}))
const filteredItems =
query === ''
? items
: items.filter((item) => {
return item.name.toLowerCase().includes(query.toLowerCase())
})
return (
<Combobox
as="div"
value={questionRange}
onChange={setQuestionRange}
className="my-5"
>
<Combobox.Label className="block text-left font-bold text-gray-700">
{label}
</Combobox.Label>
<div className="relative mt-1">
<Combobox.Input
className="w-full rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 sm:text-sm"
onChange={(event) => setQuery(event.target.value)}
displayValue={(item) => item?.name}
/>
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none">
<ChevronUpDownIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</Combobox.Button>
{filteredItems.length > 0 && (
<Combobox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{filteredItems.map((item) => (
<Combobox.Option
key={item.id}
value={item}
className={({ active }) =>
classNames(
'relative cursor-default select-none py-2 pl-3 pr-9',
active ? 'bg-indigo-600 text-white' : 'text-gray-900'
)
}
>
{({ active, selected }) => (
<>
<span
className={classNames(
'block truncate',
selected && 'font-semibold'
)}
>
{item.name}
</span>
{selected && (
<span
className={classNames(
'absolute inset-y-0 right-0 flex items-center pr-4',
active ? 'text-white' : 'text-indigo-600'
)}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
)}
</>
)}
</Combobox.Option>
))}
</Combobox.Options>
)}
</div>
</Combobox>
)
}
initialize the state of questionRange when declaring it .
const [questionRange, setQuestionRange] = useState(items[0])

react headless ui combox autocomplete, how to search by enter key

i am using this headless ui combobox https://headlessui.dev/react/combobox
for my own auto complete search box suggestion. For this case i am wondering, how can i make the combobox to send the values when i press the enter key? currently i have ways to send the value with onclick on the suggested options but i would like to have something for enter key as well
<>
<Combobox
as="div"
className="relative mb-10 mx-auto max-w-x1 rounded-x1 bg-white shadow-2x1 ring-1 ring-black/5 divide-y divide-gray-100 overflow-hidden"
value={searchCardName}
onChange={setSearchCardName}
>
<div className="flex items-center px-4">
<SearchIcon className="h-6 w-6 text-gray-500" />
<Combobox.Input
className="h-12 w-full border-0 focus:ring-0"
placeholder="Search..."
onChange={(event) => onChangeHandler(event.target.value)}
/>
</div>
<Combobox.Options className="max-h-96 py-4 text-sm overflow-y-auto">
{suggestions.map((cardName) => (
<Combobox.Option key={cardName} value={cardName}>
{({ active }) => (
<div
className={`space-x-1 px-4 py-2 ${
active ? "bg-indigo-600" : "bg-white"
}`}
onClick={() => onSuggestHandler(cardName)}
>
<span
className={`font-medium text-gray-900 ${
active ? "text-white" : "text-gray-900"
}`}
>
{cardName}
</span>
</div>
)}
</Combobox.Option>
))}
</Combobox.Options>
</Combobox>
{isResult && <Tabs />}
</>;
edit for updated, tried onKeyDown with this
<>
<Combobox as="div" className="relative mb-10 mx-auto max-w-x1 rounded-x1 bg-white shadow-2x1 ring-1 ring-black/5 divide-y divide-gray-100 overflow-hidden" value={searchCardName} onChange={setSearchCardName}>
<div className="flex items-center px-4">
<SearchIcon className="h-6 w-6 text-gray-500" />
<Combobox.Input className="h-12 w-full border-0 focus:ring-0" placeholder="Search..." onChange={(event) => onChangeHandler(event.target.value)} onKeyPress={(e) => e.key === 'Enter' && console.log("testing")}/>
</div>
<Combobox.Options className="max-h-96 py-4 text-sm overflow-y-auto">
{suggestions.map((cardName) => (
<Combobox.Option key={cardName} value={cardName}>
{({ active }) => (
<div className={`space-x-1 px-4 py-2 ${active ? 'bg-indigo-600' : 'bg-white'}`} onClick={() => onSuggestHandler(cardName)}>
<span className={`font-medium text-gray-900 ${active ? 'text-white' : 'text-gray-900'}`}>{cardName}</span>
</div>
)}
</Combobox.Option>
))}
</Combobox.Options>
</Combobox>
{isResult &&
<Tabs />}
</>
the onKeyDown is under my Combobox.Input
<Combobox.Input className="h-12 w-full border-0 focus:ring-0" placeholder="Search..." onChange={(event) => onChangeHandler(event.target.value)} onKeyPress={(e) => e.key === 'Enter' && console.log("testing")}/>
solved it i guess is using onKeyUp instead of onKeyDown.
<Combobox.Input className="h-12 w-full border-0 focus:ring-0" placeholder="Search..." onChange={(event) => onChangeHandler(event.target.value)} onKeyUp={(e) => onKeyboardHandler(e)} />

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

React-table rerender table when deleting a row

I'm using react-table in my project and I have a button to delete the row. It works fine but when the row is deleted the table is rerendered ( loses current page in pagination, the search filters, etc.) Is there a way to prevent this to happen?
My code:
const Taxis = ({data}) => {
const [taxis, setTaxis] = useState([...data])
const columns = useMemo(() => [
{
Header: 'Active',
accessor: 'active',
Cell: ({ value }) => {
return <p className="flex justify-end md:block"><Icon path={value ? mdiCheck : mdiClose} size={1} className={`md:mx-auto ${value ? 'text-green-400' : 'text-red-400'}`}/></p>
}
},
{
Header: 'Acciones',
accessor: 'actions',
Cell: ({row}) => {
return <><div className="flex justify-end md:justify-center pb-2 md:pb-0">
<div className="group relative">
<button className="flex rounded-md p-1 text-primary border border-primary hover:border-secondary hover:text-secondary mr-2">
<Icon path={mdiPencil} size={1} />
</button>
<span className="absolute hover-sibling:opacity-100 z-30 w-20 right-2 opacity-0 bg-white rounded-lg shadow-lg text-primary px-2 py-1 mt-1 border border-gray-200 text-center text-sm">
Editar taxi
</span>
</div>
<div className="relative">
<button className="flex text-blanco border border-primary hover:border-secondary rounded-md p-1 bg-primary hover:bg-secondary"
onClick={() => {/*this is my function to delete*/
const data = await fetch(`${APP_CONSTANTS.REST_API}`,{
method: "DELETE"
})
if (data.ok) {
setTaxis(taxis.filter( taxi => taxi.idtaxi !== row.original.idtaxi))
}
}}>
<Icon path={mdiDeleteForever} size={1} />
</button>
<span className="absolute hover-sibling:opacity-100 z-30 w-24 right-0 opacity-0 bg-white rounded-lg shadow-lg text-primary px-2 py-1 mt-1 border border-gray-200 text-center text-sm">
Eliminar taxi
</span>
</div>
</div>
</>
}
},
], [taxis])
return (
<>
<Table columns={columns} data={taxis} table={'Taxis'} />
</>
)
}
The docs have the answer! Namely, you have to manually prevent those things from updating, by using the autoResetX properties on the table. In this case, you'd probably want autoResetPage and autoResetFilters set to false in order to prevent those fields from updating when your dataset does.

Resources