I have a dashboard and I want to use React-Grid-Layout but I render the components only if they have been favorited. To use the gird layout each div needs a key="a" or key="b" depending on the layout defined each key needs to be different.
How can I go about giving each div an individual key? When it renders on-screen in its current form it renders two divs with the same cards in where I need it to render one div for each card.
const layout = [
{ i: "a", x: 0, y: 0, w: 1, h: 2 },
{ i: "b", x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 },
{ i: "c", x: 4, y: 0, w: 1, h: 2 },
];
render function
const userHasFavorite = (data, favIds, layout) => {
if (favIds && data) {
const filteredData = data.filter((idsArr) =>
favIds.split(",").includes(idsArr.id)
);
const keysMapped = layout.map((ids) => ids.i);
console.log(keysMapped);
return (
<div key={keysMapped}>
<PriceCard data={filteredData} />
</div>
);
} else {
return <p1>No Cards Have Been Faved</p1>;
}
};
Grid Layout
<GridLayout
className="layout"
layout={layout}
cols={12}
rowHeight={30}
width={1200}
>
{isLoading ? (
<LoadingCard />
) : (
userHasFavorite(data, favoritedIds, layout)
)}
</GridLayout>
PRICE CARD
return (
<dl className="mt-5 ml-5 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
{data.map((item) => (
<div
key={item.id}
className="flex-row bg-white hover:bg-gray-100 dark:hover:bg-purple-700 dark:bg-secondaryDark h-24 pt-2 px-4 pb-12 sm:pt-6 sm:px-6 shadow-2xl rounded-lg overflow-hidden"
>
<div className="flex h-2 ">
{/* TODO: ADD TRANSISITON */}
{cardFaved.some((cardFaved) => cardFaved.id === item.id) ? (
<MinusSmIcon
onClick={() => removeCardFavClick(item.id)}
className="ml-auto h-5 w-5 cursor-pointer fill-current text-red-600 "
/>
) : (
<PlusSmIcon
onClick={() => handleCardFavClick(item.id)}
className="ml-auto h-5 w-5 cursor-pointer fill-current text-green-600 "
/>
)}
</div>
<dt>
<div className="absolute rounded-md p-3">
<img className="h-6 w-6 mr-3" src={item.image} />
</div>
<p className="ml-16 text-sm pb-0 font-medium text-gray-500 dark:text-white truncate">
{item.name.toUpperCase()}
</p>
</dt>
<dd className="ml-16 pb-7 flex items-baseline sm:pb-7">
<p className="text-2xl font-semibold text-gray-900 dark:text-white">
{formatDollar(item.current_price)}
</p>
</dd>
</div>
))}
</dl>
);
};
export default PriceCard;
Error I'm having is the cards render 2 in one div
Error 2
Ok, so from the React-Grid-Layout official usage, You need to map() directly within the <GridLayout/> component.
So here a good suggestion:
Within the GridLayout:
WITH map()
<GridLayout className="layout" layout={layout} cols={12} rowHeight={30} width={1200}>
{isLoading
? <LoadingCard />
: layout.map(({ i }) =>
<div key={i}>
// change this from a typical function into a React component
<UserHasFavorite data={data} favIds={favoritedIds} />
</div>
)}
</GridLayout>
WITHOUT map()
<GridLayout className="layout" layout={layout} cols={12} rowHeight={30} width={1200}>
{isLoading
? <LoadingCard />
: <React.Fragment>
<div key="a"><UserHasFavorite data={data} favIds={favoritedIds} /></div>
<div key="b"><UserHasFavorite data={data} favIds={favoritedIds} /></div>
<div key="c"><UserHasFavorite data={data} favIds={favoritedIds} /></div>
</React.Fragment>}
</GridLayout>
Within the child component
const UserHasFavorite = (data, favIds) => {
if (favIds && data) {
const filteredData = data.filter((idsArr) =>
favIds.split(",").includes(idsArr.id)
);
// <div> is moved up into the GridLayout
return <PriceCard data={filteredData} />;
} else {
return <p1>No Cards Have Been Faved</p1>;
}
};
UPDATE: To handle issue of rendering multiple "similar" cards
Within the PriceCard, there is this...
return (
<dl className="mt-5 ml-5 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
{data.map((item) => (
<div
key={item.id}
className="flex-row bg-white hover:bg-gray-100 dark:hover:bg-purple-700 dark:bg-secondaryDark h-24 pt-2 px-4 pb-12 sm:pt-6 sm:px-6 shadow-2xl rounded-lg overflow-hidden"
>
</div>
)}
</dl>
)
You are mapping to display all the cards in a row. So if you updated the className="flex-column..." those two cards should render in a Column but in one <div>.
And then if you do that, you have two options:
Either you delete the <GridLayout/> and render directly like this...
// <GridLayout className="layout" layout={layout} cols={12} rowHeight={30} width={1200}>
return (
isLoading
? <LoadingCard />
: <UserHasFavorite data={data} favIds={favoritedIds} />
)
// </GridLayout>
By removing the <GridLayout/> which is used for display, you will have just two cards rendered in a column (handled with PriceCard component).
2.Or render the <PriceCard/> directly to within the like this...
// Parent component -> GridLayout
export const ParentComponent = props => {
// I'm not sure where "data" and "favoritedIds" are coming from yet,
// but we check like "data &&..." to ensure they are defined
// before we attempt to filter
const filteredData = data && data.filter(
(idsArr) => favoritedIds && favoritedIds.split(",").includes(idsArr.id)
);
return (
<GridLayout className="layout" layout={layout} cols={12} rowHeight={30} width={1200}>
{isLoading
? <LoadingCard />
: (!!data && !!filteredData)
? <React.Fragment>
<div key="a"><PriceCard data={filteredData[0]} /></div>
<div key="b"><PriceCard data={filteredData[1]} /></div>
<div key="c"><PriceCard data={filteredData[2]} /></div>
</React.Fragment>}
: <p1>No Cards Have Been Faved</p1>
</GridLayout>
)
}
// Child component -> PriceCard
export const ChildComponent = props => {
return (
<dl className="mt-5 ml-5 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
{data && /* don't map here -> we moved it to parent */
<div className="flex-row bg-white hover:bg-gray-100 dark:hover:bg-purple-700 dark:bg-secondaryDark h-24 pt-2 px-4 pb-12 sm:pt-6 sm:px-6 shadow-2xl rounded-lg overflow-hidden">
/* make necessary changes */
</div>}
</dl>
)
}
From what I read I think that you need to iterate and assign it to the div. You can do something like this.
return (
{filteredData.map(data => (
<div key={data.i}>
<PriceCard data={filteredData} />
</div>
)
);
Related
I'm using useFieldArray from react-hook-form to replicate my requirements component, but I need to get the return value from the requirementValueCalc function and pass it to my form component. In it I want to sum the values of each inserted field. Can someone help me?
enter image description here
My form - parent component:
const ThirdPage: ForwardRefRenderFunction<
HTMLSelectElement,
ThirdPageProps
> = ({
formProps: {
register,
formState: { errors },
control,
setValue,
setFocus,
},
formData,
secondPageData,
}) => {
const {
dealId,
object,
contractPeriod,
paymentMethodId,
dueDay,
totalHours,
totalValue,
} = formData[0];
const [currentIndex, setCurrentIndex] = useState<number>(0);
const [requirements, setRequirements] = useState<RequerimentType[]>([]);
const { append, remove, fields, insert } = useFieldArray({
name: 'requirements',
control,
});
const requirementOptions = requirements?.map(
useCallback(
(requirement) => {
return {
id: requirement.id,
name: `${requirement.title} (${Math.floor(
requirement.estimate / 8
)} dias)`,
value: requirement.title,
effort: requirement.estimate,
coefficient: requirement.coefficient,
};
},
[requirements]
)
);
const handleKeyPress = (event: KeyboardEvent) => {
if (
(event.ctrlKey || event.metaKey) &&
event.key === 'ArrowUp' &&
currentIndex > 0
) {
insert(currentIndex, {});
}
if ((event.ctrlKey || event.metaKey) && event.key === 'ArrowDown') {
insert(currentIndex + 1, {});
}
if (
(event.ctrlKey || event.metaKey) &&
event.key === 'Backspace' &&
currentIndex > 0
) {
remove(currentIndex);
setFocus(`requirements.${currentIndex - 1}.title`);
}
};
useEffect(() => {
document.addEventListener('keydown', handleKeyPress);
return () => {
document.removeEventListener('keydown', handleKeyPress);
};
}, [handleKeyPress]);
useEffect(() => {
const getAllRequirements = async () => {
try {
const response = await listAllRequirements();
setRequirements(response.data.items);
} catch (err) {
console.log(err.response.data);
}
};
getAllRequirements();
append({});
}, []);
return (
<>
<div className='flex flex-col divide-y divide-zinc-700 divide-opacity-25 px-6 last:border-0 sm:px-2'>
<div className='flex flex-col'>
<div className='flex flex-row items-center justify-between gap-10'>
<Input
placeholder='Id da oportunidade vem do RD'
label='Id da oportunidade'
withoutBorder
error={errors.dealId}
{...register('dealId')}
defaultValue={dealId}
/>
<Input
placeholder='Objeto'
label='Objeto'
withoutBorder
error={errors.object}
{...register('object')}
defaultValue={object}
/>
</div>
<div className='flex flex-row items-center justify-between gap-10 pt-6'>
<Input
placeholder='Período do contrato'
label='Período do contrato'
withoutBorder
error={errors.contractPeriod}
{...register('contractPeriod')}
defaultValue={contractPeriod}
/>
<Input
placeholder='Método de pagamento'
label='Método de pagamento'
withoutBorder
error={errors.paymentMethodId}
{...register('paymentMethodId')}
defaultValue={paymentMethodId}
disabled
/>
</div>
<div className='flex items-center justify-between gap-10 pt-6'>
<Input
placeholder='Dia de vencimento da parcela'
label='Dia de vencimento da parcela'
type='number'
withoutBorder
error={errors.dueDay}
{...register('dueDay')}
defaultValue={dueDay}
/>
<div className='flex w-full flex-row items-center justify-between gap-10'>
<div className='flex w-full justify-end'>
<input
label='Total de horas'
id='totalHours'
type='number'
hidden
error={errors.totalHours}
{...register('totalHours')}
defaultValue={totalHours}
/>
</div>
<div className='flex w-full flex-col items-start gap-2'>
<input
id='totalValue'
hidden={true}
defaultValue={totalValue}
{...register('totalValue')}
/>
</div>
</div>
</div>
<hr className='mt-8 border-dark-300 pt-5' />
<div className='flex w-full justify-between'>
<h1 className='font-bold text-primary-600'>Requisitos</h1>
<Tooltip />
</div>
{fields.map((field, index) => (
<Requirement
fieldId={field.id}
errors={errors}
index={index}
register={register}
setCurrentIndex={setCurrentIndex}
requirementOptions={requirementOptions}
setValue={setValue}
currentIndex={currentIndex}
hourValue={secondPageData.hourValue}
/>
))}
<div className='flex w-full justify-end'>
<div className='flex-col'>
<div className='flex justify-end'>
<h6 className='py-2 text-white'>{`200 / horas`}</h6>
</div>
<div className='flex justify-end'>
<Title size={'bold'}>{`R$ teste`}</Title>
</div>
<div className='flex justify-end'>
<h6 className='py-2 text-white'>
<b>4 sprint</b> / ORÇADO
</h6>
</div>
<div className='flex justify-end'>
<h6 className='py-2 text-white'>
<b>{`test dias úteis`}</b> / CONTRATO
</h6>
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default ThirdPage;
My requirement component - child component:
const Requirement = ({
hourValue,
fieldId,
errors,
index,
register,
setCurrentIndex,
requirementOptions,
setValue,
currentIndex,
}: SelectProps) => {
const [coefficient, setCoefficient] = useState<number>();
const [effort, setEffort] = useState<number>();
const requirementValueCalc = (effort: number, factor: number, hourValue: number) => {
const result = effort * factor * hourValue;
return result;
};
return (
<>
<div key={fieldId} className='flex flex-col gap-3'>
<div className='flex flex-row items-center justify-start gap-10'>
<div>
<Input type={'checkbox'}></Input>
</div>
<div className='w-2/6'>
<div>
{errors.requirements?.[index].title?.message && (
<span className='text-xs text-error-900'>
{errors.requirements?.[index].title?.message}
</span>
)}
</div>
<Input
id='requirementTitle'
placeholder='Título do requisito'
withoutBorder
{...register(`requirements.${index}.title`)}
onFocus={() => setCurrentIndex(index)}
className={classNames(
errors.requirements
? 'border-error-900 px-3 py-2 placeholder-error-900 focus:border-error-900 focus:ring-error-900'
: 'border-transparent px-3 py-2 placeholder-dark-300 focus:border-primary-500 focus:ring-0',
'block w-full appearance-none rounded-md border',
'bg-dark-900 text-white caret-primary-600',
'focus:outline-none sm:text-sm'
)}
/>
</div>
<div className='flex w-1/4 gap-3'>
{requirementOptions.length > 0 && (
<>
<Select
handleSetValue={(value) =>
setValue(
`requirements.${index}.typeRequirementId`,
value.id,
setCoefficient(value.coefficient),
setEffort(value.effort),
)
}
options={requirementOptions}
/>
<div className='flex ml-8 w-full gap-3'>
<Title size={'bold'}>{`R$ ${requirementValueCalc(coefficient, effort, hourValue)}`}</Title>
</div>
</>
)}
</div>
</div>
<div className='mb-5 flex flex-col gap-2'>
<div>
{errors.requirements?.[index].description?.message && (
<span className='text-xs text-error-900'>
{errors.requirements?.[index].description?.message}
</span>
)}
</div>
<textarea
id='description'
rows={2}
placeholder='Descrição do requisito'
className={classNames(
errors.requirements
? 'border-error-900 px-3 py-2 placeholder-error-900 focus:border-error-900 focus:ring-error-900'
: 'border-transparent px-3 py-2 placeholder-dark-300 focus:border-primary-500 focus:ring-0',
'block w-full appearance-none rounded-md border',
'bg-dark-900 text-white caret-primary-600',
'focus:outline-none sm:text-sm'
)}
{...register(`requirements.${index}.description`)}
/>
</div>
<hr className={currentIndex ? 'border-dark-300 pb-5' : 'invisible'} />
</div>
</>
);
};
export { Requirement };
I tried to solve it, taking the child's values and sending them to the parent with a callback, but I'm not managing to keep the dynamic elements with their indexes.
I have a child and parent compoinents as follows,
When I click on the div I am not able to get the alert in parent component but when I keep button in child component I can able to get the aler. Not sure what I am doing, Can any one please help. Thanks.
Child Comp:
const PrimaryNavigation = ({ className, data, childToParent }: IPrimaryNavigation) => {
return (
<div className={`${className} w-full bg-black grid grid-cols-12 gap-4 py-12 pl-12`}>
<div className="relative col-span-2 md:col-span-1 w-5 h-5 md:w-6 md:h-6 cursor-pointer" onClick={() => { childToParent() }}>>
<Image src={searchSvg} layout="fill" objectFit="contain" alt="hamburgerIcon" />
</div>
<div className="relative col-span-2 md:col-span-1 md:w-6 md:h-6 w-5 h-5 cursor-pointer" >
<Image src={hamburgerSvg} layout="fill" objectFit="contain" alt="searchIcon" />
</div>
</div>
)
}
Parent Comp:
const Header = ({ data }: IHeader) => {
const childToParent = () => {
alert('hi')
}
return (
<div>
<header className="w-full md-0 md:md-6 bg-black grid grid-cols-12 gap-4">
<PrimaryNavigation className="col-span-11" data={data.primaryNavigationCollection.items} childToParent={childToParent} />
</header>
</div>
)
}
export default Header
I'm implementing a modal popup for a youtube in my Next app. I'm mapping through json object and passing them into cards. and when user selects specific card it displays a modal popup. and in that popup it is passed a url for a youtube vid.
my index.js code.
const Contents = ({result , index}) => {
let router = useRouter();
const [modal, setModel] = useState(false);
const [temp, setTemp] = useState([]);
const getData = (vid)=>{
let temp = [vid];
setTemp(item =>[1, ...temp]);
return setModel(true);
}
//console.log(result.items[1].snippet.title);
return (
<div className='grid md:grid-cols-2 gap-y-6 place-items-center lg:grid-cols-4 px-5 pt-5'>
{result.items && result.items.map((res, idx)=>{
//console.log(res.snippet);
return (
<div>
{/* <ContentCard result={res} index={index} key={idx}/> */}
<div>
<div onClick={()=>getData(res.snippet.resourceId?.videoId)}>
<div className='flex flex-col hover:scale-110 hover:transition-all hover:duration-200 hover:ease-in ease-out duration-200'>
<div className=' relative snap-center rounded-lg h-40 w-64 '>
<Image
src={res.snippet.thumbnails.standard?.url || res.snippet.thumbnails.medium?.url || res.snippet.thumbnails.high?.url}
layout="fill"
objectFit="cover"
className={`h-full w-full bg-cover rounded-lg bg-no-repeat`} />
</div>
<Moment className=' text-dark-gray' date={res.snippet.publishedAt} format={"MMM Do YY"}/>
<div className=' text-sm font-thin font-serif text-med-gray'>{res.snippet.title.slice(0, 25).concat('...')}</div>
</div>
</div>
</div>
{
modal === true ? <Modal vid={temp[1]} />: ''
}
</div>
)
})}
</div>
)
}
and my modal.js
return(
<div className=" fixed inset-0 bg-bg-black bg-opacity-70 backdrop-blur-sm flex justify-center items-center">
<div className="">
<Video vid={vid} />
{/* <h1>{vid}</h1> */}
</div>
</div>
)
Video.js
const opts ={
height: '70%',
wresulthth: '70%',
playerVars: {
autoplay: 1,
}
}
return (
<YouTube videoId={vid} opts={opts}/>
);
my issue is when modal is fired up it displays the prop data, but when i use it for youtube videos it doesn't work. also uses a lot of my cpu and it lags when only modal is opened. what is the fix?
So I have an overlay flex container that when clicked adds nominations to a movie. How do I make it so that it doesn't appear and thus doesn't give the option to click it once a movie has already been nominated? The click event is located in the MovieList container where the handleNominationsClick function is being taken from props and adding it to the onClick property in the overlay.
Here is App:
const [nominations, setNominations] = useState([]);
const nominateMovie = (movie) => {
const newNominationList = [...nominations, movie];
setNominations(newNominationList);
};
return (
<div className='container-fluid movie-app'>
<div className='row d-flex align-items-center mt-4 mb-4'>
<MovieListHeading heading='Movies' />
<SearchBox searchValue={searchValue} setSearchValue={setSearchValue} />
</div>
<div className='row'>
<MovieList
movies={movies}
nominationComponent={AddNominations}
handleNominationsClick={nominateMovie}
/>
</div>
<div className='row d-flex align-items-center mt-4 mb-4'>
<MovieListHeading heading='Nominations' />
</div>
MovieList Component:
onst MovieList = (props) => {
const NominationComponent = props.nominationComponent;
return (
<>
{props.movies.map((movie, index) => (
<div className='image-container d-flex justify-content-start m-3 col-3 d-flex flex-column'>
<img src={movie.Poster} alt='movie'></img>
<div
onClick={() => props.handleNominationsClick(movie)}
className='overlay d-flex align-items-center justify-content-center'
>
<NominationComponent />
</div>
</div>
))}
</>
);
};
I suggest storing the nominated movies in an object that provides O(1) lookup and pass the nominations as a prop to MovieList so the overlay div can be conditionally rendered. I am assuming your movies data has an id property (or some uniquely identifying property) to use. If your data doesn't have a unique identifier then I highly recommend augmenting it to include one so you can use a proper React key when mapping.
App
const [nominations, setNominations] = useState({}); // <-- object
const nominateMovie = (movie) => {
setNominations(nominations => ({
...nominations,
[movie.id]: true // <-- set movie nomination by id
}));
};
return (
<div className='container-fluid movie-app'>
<div className='row d-flex align-items-center mt-4 mb-4'>
<MovieListHeading heading='Movies' />
<SearchBox searchValue={searchValue} setSearchValue={setSearchValue} />
</div>
<div className='row'>
<MovieList
movies={movies}
nominationComponent={AddNominations}
nominations={nominations} // <-- pass nominations
handleNominationsClick={nominateMovie}
/>
</div>
MovieList
const MovieList = (props) => {
const NominationComponent = props.nominationComponent;
return (
<>
{props.movies.map((movie, index) => (
<div className='image-container d-flex justify-content-start m-3 col-3 d-flex flex-column'>
<img src={movie.Poster} alt='movie'></img>
{!props.nominations[movie.id] && ( // <-- conditional render
<div
onClick={() => props.handleNominationsClick(movie)}
className='overlay d-flex align-items-center justify-content-center'
>
<NominationComponent />
</div>
)}
</div>
))}
</>
);
};
I'm trouble and needing help from someone more senior.
I need to render a modal component when the user clicks in a link inside a Formik form. But that form already is a component, so I'm trying to render a component inside another component that is inside a bigger component. But isn't working.
The modal or still open forever (from loading the page) or didn't load at all.
Here's the code that I tried.
Form:
// Dependencies
// $FlowExpectedError[cannot-resolve-module] */
import { useRouter } from "next/router";
// $FlowExpectedError[cannot-resolve-module] */
import { AnimatePresence, motion } from "framer-motion";
// $FlowExpectedError[cannot-resolve-module] */
import { setCookie } from "nookies";
import { Modal } from "../components/Common";
// Components
// $FlowExpectedError[cannot-resolve-module] */
import { ErrorMessage, Formik, Field, Form } from "formik";
import {
Button,
Label,
FormField,
FormCheckbox,
HeadLogo,
} from "../components/Common";
// Helpers
import { axiosRequest } from "../helpers/axiosRequest";
import React, { useState } from "react";
import literals from "../utils/literals";
const GENDERS = [
{
label: "Female",
val: "female",
},
{
label: "Male",
val: "male",
},
{
label: "Other",
val: "other",
},
];
const ModalCloseButton = ({ closeCallback }) => (
<div>
<button
type="button"
className="float-right p-5 focus:outline-none"
onClick={closeCallback}
>
{/* $FlowExpectedError[cannot-resolve-name] */}
<img
src="/icons/close.svg"
alt="Close icon"
className="float-right w-4"
/>
</button>
</div>
);
const TermsModal = ({ closeCallback }) => (
// $FlowExpectedError[cannot-resolve-name]
<>
<ModalCloseButton closeCallback={closeCallback} />
<span className="px-5 pb-5 text-lg font-bold leading-none">
{literals.SEND_TO_FRIEND}
</span>
<div className="botton justify-center">
<Button label={literals.ACCEPT} onClick={closeCallback} style="icon" />
</div>
</>
);
const SignupForm = ({
validateForm,
submitForm,
setCheckedGender,
checkedGender,
openModal,
cancelModal,
}) => (
<Formik
initialValues={{
name: "",
surnames: "",
email: "",
phone: "",
birthDate: "",
gender: "",
password: "",
terms: false,
}}
validate={validateForm}
onSubmit={submitForm}
>
{({ isSubmitting, setFieldValue, values }) => {
function handleGenderSelection(value) {
setCheckedGender(value);
setFieldValue("gender", value);
}
return (
<Form className="flex flex-col w-full space-y-2">
{/* $FlowExpectedError[cannot-resolve-name] */}
<FormField
name="name"
inputType="text"
className="text-input-bis focus:outline-none"
inputPlaceholder={literals.NAME}
/>
<FormField
name="surnames"
inputType="text"
className="text-input-bis focus:outline-none"
inputPlaceholder={literals.SURNAME}
/>
<FormField
name="email"
inputType="email"
className="text-input-bis focus:outline-none"
inputPlaceholder={literals.EMAIL}
/>
<FormField
name="password"
inputType="password"
className="text-input-bis focus:outline-none"
inputPlaceholder={literals.PASSWORD}
/>
<FormField
name="phone"
inputType="tel"
className="text-input-bis focus:outline-none"
inputPlaceholder={literals.PHONE_NUMBER}
/>
<FormField
name="birthDate"
inputType="date"
className="text-input-bis focus:outline-none"
inputPlaceholder={literals.BIRTHDATE}
/>
<div role="group" className="signup-radio">
{GENDERS.map((gender) => {
return (
<label
key={gender.val}
className={`
px-4 pt-3 pb-2 mr-2 border border-white rounded-gender leading-snug transition-colors duration-200
${
checkedGender && gender.val === checkedGender
? "bg-lightBlue"
: "transparent"
}
`}
>
<Field
type="radio"
name="gender"
value={gender.val}
checked={checkedGender === gender.val}
onChange={() => {
handleGenderSelection(gender.val);
}}
/>
<p className="text-sm text-white uppercase">{gender.label}</p>
</label>
);
})}
<ErrorMessage
name="gender"
component="div"
className="error-message"
/>
</div>
<div className="flex flex-row items-center justify-start">
<FormCheckbox
label={
<p className="ml-2 text-sm text-lightBlue">
{literals.ACCEPT_THE}
<a
href="#"
className="text-white underline"
onClick={cancelModal()}
>
{literals.TERMS_TITLE}
</a>
</p>
}
name="terms"
callback={() => setFieldValue("terms", !values.terms)}
callbackValue={!values.terms}
fillColor="lightBlue"
value={values.terms}
marginBottom="4"
/>
</div>
<div className="pb-4">
<Button type="submit" label={literals.SIGNUP} />
</div>
{/* <div className="flex flex-col items-stretch justify-start h-vh-5 flex-1 px-5 pb-5">
<div className="flex flex-col flex-1 mt-12">
<a
href={literals.GLOBAL_TERMS}
target="_blank"
className="text-xl font-bold leading-tight"
>
<u>Download</u> our Terms & Conditions
</a>
</div>
<div className="botton justify-center">
<Button
label={literals.ACCEPT}
onClick={() => setShowModal(false)}
style="icon"
/>
</div>
</div>
</Modal>
</AnimatePresence> */}
</Form>
);
}}
</Formik>
);
function Signup(): React$Node {
const [checkedGender, setCheckedGender] = useState();
const [showModal, setShowModal] = useState();
function cancelModal() {
console.log("fechar");
setShowModal(false);
}
function openModal() {
console.log("abrir");
setShowModal(true);
}
// TODO: Review validation
function validateForm(values) {
const errors = {};
if (!values.name) {
errors.name = "Required";
}
if (!values.surnames) {
errors.surnames = "Required";
}
if (!values.email) {
errors.email = "Required";
}
if (!values.password) {
errors.password = "Required";
}
if (!values.phone) {
errors.phone = "Required";
}
if (!values.birthDate) {
errors.birthDate = "Required";
}
if (!values.gender) {
errors.gender = "Required";
}
if (!values.terms) {
errors.terms = "Required";
}
return errors;
}
async function submitForm(body, { setErrors }) {
const res = await axiosRequest({
method: "POST",
url: "/auth/register",
data: body,
});
if (res?.accessCookie) {
const accessCookie = res.accessCookie;
const refreshCookie = res.refreshCookie;
setCookie(null, "accessToken", accessCookie.token, accessCookie.options);
setCookie(
null,
"refreshToken",
refreshCookie.token,
refreshCookie.options
);
router.reload();
} else {
const errorData = res?.error?.response?.data || {};
setErrors({ [errorData.field]: errorData.error });
}
}
return (
<motion.div
className="flex flex-col min-h-full"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<HeadLogo />
<div className="grid w-full min-h-screen grid-cols-4 gap-4 px-5 rounded-background bg-purple">
<div className="col-span-3 col-start-1 text-white mt-28">
<h1 className="w-full mb-3 font-bold leading-none text-40px">
{literals.SIGNUP_TITLE}
</h1>
<p className="w-full text-base font-light">
{literals.SIGNUP_SUBTITLE}
</p>
</div>
<div className="flex-col w-full col-span-4">
{/* $FlowExpectedError[cannot-resolve-name] */}
<SignupForm
validateForm={validateForm}
submitForm={submitForm}
setCheckedGender={setCheckedGender}
checkedGender={checkedGender}
openModal={openModal}
cancelModal={cancelModal}
/>
<Modal cancelCallback={cancelModal} closeCallback={cancelModal}>
<TermsModal closeCallback={cancelModal} />
</Modal>
</div>
</div>
</motion.div>
);
}
export default Signup;
This is the component Modal:
// #flow
import { Button } from "./Button";
// $FlowExpectedError[cannot-resolve-module]
import { motion } from "framer-motion";
type Props = {
children: any,
cancelCallback?: function,
closeCallback: function,
}
export const Modal = ({
children,
cancelCallback,
closeCallback,
}: Props): React$Node => {
return (
// $FlowExpectedError
<motion.div
className="fixed inset-0 z-50 px-5 py-16 overflow-hidden bg-darkBlue bg-opacity-90 text-darkBlue"
onClick={cancelCallback || closeCallback}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
{/* $FlowExpectedError */}
<motion.div
className="flex flex-col items-stretch justify-start max-h-full overflow-y-auto bg-white rounded-card"
onClick={e => e.stopPropagation()}
initial={{ y: 64, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 0, opacity: 0 }}
transition={{
type: "spring",
duration: 0.5,
bounce: 0,
}}
>
{children}
</motion.div>
</motion.div>
);
};
I don't really understand what you are trying to do. If you like to define const you can do it.
In the "component Modal:" just before return, you can define const and useState or whatever you want. But these constants will be accessible only in <Modal>.
Of course, there are components that must be on the top of the tree, for example, some initial constants and props.
P.S in nextjs you have <Link> component to your internal website routes:
<Link href={'/homepage'}>
some stuff
</Link>
You can share some screens of behaviour for future investigation.