Repository navigation
|
Im currently making a product form that uses The process has proven to be a little difficult because it seems like these two libraries clash with one-another. Inside the parent component i am mapping all the products in the fieldArray const { control } = useFormContext()
const { fields, remove, append } = useFieldArray({ control, name: 'main.products', keyName: 'field_id' })
const products: InvoiceProduct[] = useWatch({ name: 'main.products' }) || []
}
const fls = fields.map((fl, index) => ({
field: fl,
index: index
}))
<AnimatePresence>
{ fls.map(fl => {
const prodId = products[fl.index]?.id
return (
<ProductInputBlock
key={prodId || fl.field.field_id}
index={fl.index}
hookVal='main.products'
onDelete={!prodId ? () => remove(fl.index) : undefined}
lastElem={products?.length - 1}
/>
)
}) }
</AnimatePresence>The problem is happening inside my <motion.div
layout='position'
variants={blockVariants}
initial='initial'
animate='animate'
exit='exit'
className='rounded-[12px] bg-lighter-blue p-5 border border-blue-border'
onAnimationComplete={(e) => {
if (e === 'exit' && index === lastElem && !!onDelete) {
onDelete()
}
}}
>
<div className='flex justify-between items-center mb-4'>
<p className='text-link-blue font-semibold text-xs uppercase mb-1'>
{ t('product') }
<span className='text-lg'> #{ index + 1 }</span>
</p>
{ !!onDelete && <DeleteButton onDelete={onDelete} /> }
</div>
<div className='flex gap-4'>
<SpecialInput
hookVal={`${name}.name_client`}
placeholder={t('product-name')}
divClass='w-1/2'
{...inputProps}
/>
<SpecialInput
type='price'
hookVal={`${name}.price`}
{...inputProps}
/>
<div>
<Controller
name={`main.products_curr`}
render={({ field }) => <NewPopover
trigger={<button className='text-headerblue'>{ field.value || 'Valuta' }</button>}
>
{ currencies.map(curr => <PopoverButton onClick={() => field.onChange(curr.curr)} key={`curr-${curr.curr}`}>
{ curr.curr }
</PopoverButton>) }
</NewPopover>}
/>
</div>
<p className='mt-3 text-gray-500 text-sm'>X</p>
<SpecialInput
hookVal={`${name}.qty`}
type='number'
divClass='w-[100px]'
{...inputProps}
/>
</div>
<SpecialInputTextarea
hookVal={`${name}.description`}
placeholder={t('product-desc')}
{...inputProps}
/>
</motion.div>I have to use the Because of this, on If anyone has a solution to this I would be very very grateful. |
Replies: 2 comments 1 reply
|
The flicker is caused by the element's key changing after the refresh. Before persistence the key is There is a second issue: const { fields, remove } = useFieldArray({
control,
name: "main.products",
keyName: "field_id",
});
<AnimatePresence initial={false} mode="popLayout">
{fields.map((field, index) => (
<ProductInputBlock
key={field.field_id}
index={index}
hookVal="main.products"
onDelete={() => remove(index)}
/>
))}
</AnimatePresence>Then the animated wrapper only needs to describe its lifecycle: <motion.div
layout="position"
variants={blockVariants}
initial="initial"
animate="animate"
exit="exit"
>
<DeleteButton onDelete={onDelete} />
{/* fields */}
</motion.div>Keep the React key tied to the stable key generated by |
|
I found the fix. Triggering the removal on the end of the exit animation fixes it. What i needed to do was not call the Instead, I added an const [isDeleting, setIsDeleting] = useState(false)
function handleDelete() {
setIsDeleting(true)
}
<motion.div
layout='position'
variants={blockVariants}
initial='initial'
animate={isDeleting ? 'exit' : 'animate'}
className='rounded-[12px] bg-lighter-blue p-5 border border-blue-border'
onAnimationComplete={(e) => {
if (e === 'exit' && isDeleting) {
onDelete?.()
}
}}
>
...
{ !!onDelete && <DeleteButton onDelete={handleDelete} /> }
...
</motion.div>
|
I found the fix. Triggering the removal on the end of the exit animation fixes it. What i needed to do was not call the
remove()function twice (once when the button was clicked and another time when the exit animation triggered).Instead, I added an
isDeletingstate variable and set it to true when the button was clicked: