提问人:Jose Pablo Arancibia Linker 提问时间:11/7/2023 更新时间:11/7/2023 访问量:107
为什么我无法使用 Tanstack React Table 获取单行的值?
Why can't I get the values of a single row using Tanstack React Table?
问:
我正在使用 NextJS 13 进行一个项目。我有一个Tanstack数据表,我正在使用单元格操作来编辑或删除一行。
当我单击“修改”按钮(蓝色)时,它会打开一个模式来编辑与该行对应的字段。
但是,问题在于,无论我选择修改哪一行,它始终在其相应的输入中显示最后一行的数据。
DataTable 列定义:
import { ColumnDef } from '@tanstack/react-table'
import ExpenseActions from './ExpenseActions';
export type Expense = {
id: string;
description: string;
value: number;
date_of_expense: Date;
}
export const columnsExpenses: ColumnDef<Expense>[] = [
{
accessorKey: 'description',
header: 'Descripcion',
},
{
accessorKey: 'value',
header: 'Gasto',
cell: ({ row }) => {
const amount = parseFloat(row.getValue('value'))
const formatted = new Intl.NumberFormat('es-CL', {
style: 'currency',
currency: 'CLP'
}).format(amount);
return <div className='text-right font-medium'>{formatted}</div>
}
},
{
accessorKey: 'date_of_expense',
header: 'Fecha Gasto',
cell: ({ row }) => {
const day = row.original.date_of_expense.getDate();
const month = row.original.date_of_expense.getMonth() + 1;
const year = row.original.date_of_expense.getFullYear();
return `${day}/${month}/${year}`;
}
},
{
id: 'actions',
header: 'Acciones',
cell: ({ row }) => (
<ExpenseActions
expenseId={row.original.id}
value={row.original.value}
description={row.original.description}
dateOfExpense={row.original.date_of_expense}
/>
)
}
]
ExpenseActions.tsx:
'use client'
import { Button } from '@/components/ui/button';
import axios from 'axios';
import { Expense } from '@prisma/client';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import toast from 'react-hot-toast';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { useModal } from '@/hooks/useModal';
import EditExpenseModal from '@/components/modal/EditExpenseModal';
import { Row } from '@tanstack/react-table';
import { Payment } from './columns';
interface ExpenseActionsProps {
expenseId: string;
value: number;
description: string;
dateOfExpense: Date;
}
const ExpenseActions = ({
expenseId,
value,
description,
dateOfExpense
}: ExpenseActionsProps) => {
const router = useRouter();
const { onOpen, isOpen } = useModal();
const deleteExpense = async () => {
const isLoading = toast.loading('Eliminando...');
await axios.delete(`/api/expense/${expenseId}`)
.then(() => {
toast.dismiss(isLoading);
toast.success('Item eliminado con éxito!');
router.refresh();
})
.catch(() => {
toast.error('Error al eliminar item. Por favor intentelo denuevo.');
})
.finally(() => {
toast.dismiss(isLoading);
})
}
return (
<>
<EditExpenseModal
expenseId={expenseId}
value={value}
description={description}
dateOfExpense={dateOfExpense}
/>
<div className='flex justify-center gap-x-2'>
<AlertDialog>
<AlertDialogTrigger className='bg-red-700 rounded-lg p-2 text-sm'>Eliminar</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>¿Está seguro que quiere eliminar el siguiente gasto?: {value}.</AlertDialogTitle>
<AlertDialogDescription>
Está acción no se puede deshacer.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<Button onClick={deleteExpense} variant='destructive'>Eliminar</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<Button id='edit-expense' onClick={() => { onOpen('editExpense') }} size='sm' variant='primary'>
Modificar
</Button>
</div>
</>
);
}
export default ExpenseActions;
EditExpenseModal.tsx:
import * as z from 'zod';
import { useEffect, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { cn } from '@/lib/utils';
import axios from 'axios';
import { useForm } from 'react-hook-form';
import { format } from 'date-fns';
import { useModal } from '@/hooks/useModal';
import { CircleDashed, DollarSign, CalendarIcon } from 'lucide-react';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '../ui/dialog';
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '../ui/form';
import { Calendar } from '../ui/calendar';
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
import { Label } from '../ui/label';
import { Input } from '../ui/input';
import { Button } from '../ui/button';
interface EditExpenseModalProps {
expenseId:string;
value: number;
description: string;
dateOfExpense: Date;
}
const formSchema = z.object({
value: z.coerce
.number({
required_error: 'Valor Requerido.',
})
.int({
message: 'El valor debe ser un numero.'
}),
description: z.string().min(5, { message: 'La descripcion debe tener al menos 5 carácteres' }),
dateOfExpense: z.date({
required_error: 'Se requiere una fecha del gasto que realizó'
})
})
const EditExpenseModal = ({
expenseId,
value,
description,
dateOfExpense
}: EditExpenseModalProps) => {
const [isMounted, setIsMounted] = useState(false);
const { isOpen, onClose, type, onOpen } = useModal();
const isModalOpen = isOpen && type === 'editExpense';
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
value: value,
description: description,
dateOfExpense:dateOfExpense
},
})
const isSubmitting = form.formState.isSubmitting;
const onSubmit = async (values: z.infer<typeof formSchema>) => {
await axios.patch(`/api/expense/${expenseId}`, values);
form.reset();
onClose();
window.location.reload();
}
useEffect(() => {
setIsMounted(true);
}, []);
if (!isMounted) {
return null;
}
return (
<Dialog open={isModalOpen} onOpenChange={onClose}>
<DialogContent className='bg-white text-black p-0 overflow-hidden dark:bg-[#313338]'>
<DialogHeader className='pt-8 px-6'>
<DialogTitle className='text-2xl text-center font-bold text-black dark:text-white'>
Modificar Gasto.
</DialogTitle>
</DialogHeader>
<div className='p-6'>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className='text-black dark:text-white'>
<FormField
control={form.control}
name='value'
render={({ field }) => (
<FormItem>
<FormLabel className='text-lg'>Valor Gastado.</FormLabel>
<div className='flex flex-row items-center'>
<DollarSign size={20} className='absolute' />
<FormControl>
<Input
type='number'
placeholder='Ingrese la cantidad que gastó'
defaultValue={value}
className='pl-6 pb-2'
disabled={isSubmitting}
{...field}
/>
</FormControl>
</div>
<FormMessage className='text-red-600' />
</FormItem>
)}
/>
<FormField
control={form.control}
name='description'
render={({ field }) => (
<FormItem className='pt-5'>
<FormLabel className='text-lg'>Descripcion del Gasto.</FormLabel>
<FormControl>
<Input
placeholder='Indique pequeña descripcion de lo que gastó'
defaultValue={description}
disabled={isSubmitting}
{...field}
/>
</FormControl>
<FormDescription>
Ingrese el nombre del producto o una pequeña descripcion de lo que gastó.
</FormDescription>
<FormMessage className='text-red-600' />
</FormItem>
)}
/>
<FormField
control={form.control}
name="dateOfExpense"
render={({ field }) => (
<FormItem className="flex flex-col mt-5">
<FormLabel className='text-lg'>Fecha del Gasto.</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
id='calendar'
variant="outline"
className={cn(
"w-[240px] pl-3 text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>Seleccione una fecha.</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={dateOfExpense}
onSelect={field.onChange}
disabled={(date) =>
date > new Date() || date < new Date("1900-01-01")
}
initialFocus
/>
</PopoverContent>
</Popover>
<FormDescription>
Seleccione la fecha en la que realizó el gasto.
</FormDescription>
<FormMessage className='text-red-600' />
</FormItem>
)}
/>
<DialogFooter className='px-6 py-4'>
{
isSubmitting ? (
<>
<Button variant='primary' disabled>
<CircleDashed size={20} className='mx-2 animate-spin' />
Modificando...
</Button>
</>
) : (
<Button variant='primary' type='submit'>
Modificar
</Button>
)
}
</DialogFooter>
</form>
</Form>
</div>
</DialogContent>
</Dialog>
);
}
export default EditExpenseModal;
模态组件仅获取 Table 的最后值。但是,当我单击“删除”按钮(红色)时,打印的值将获得正确的行值。
我在这里错过了什么?
提前非常感谢你。
答:
1赞
Beast80K
11/7/2023
#1
问题:
但是,问题在于,无论我选择修改哪一行,它始终在其相应的输入中显示最后一行的数据。 模态组件仅获取 Table 的最后值。
可能原因:
不传递其他值,
溶液:
检查此行在您的文件中:ExpenseActions.tsx
<AlertDialogTitle>¿Está seguro que quiere eliminar el siguiente gasto?: {value}.</AlertDialogTitle>
如果你仔细看代码,你还没有在刚刚使用过的其他道具{expenseId,description,dateOfExpense}
<AlertDialog>
{value}
关于:
但是,当我单击“删除”按钮(红色)时,打印的值将获得正确的行值。
说明:看看你已经过去了,DataTable columns definition
{
id: 'actions',
header: 'Acciones',
cell: ({ row }) => (
<ExpenseActions
expenseId={row.original.id}
value={row.original.value}
description={row.original.description}
dateOfExpense={row.original.date_of_expense}
/>
)
}
在你有道具ExpenseActions.tsx
{
expenseId,
value,
description,
dateOfExpense
}
deleteExpense
函数正在使用该道具
await axios.delete(`/api/expense/${expenseId}`)
如果您仍然有疑问,或者我没有解决您的问题,请发表评论(我将更新答案)。
评论
0赞
Jose Pablo Arancibia Linker
11/7/2023
我想我理解你的答案,但是我不明白为什么 <AlertDialog></AlertDialog> 中的“{value}”会正确获取行值,并且当我将这些相同的值传递给我的模态组件时,它总是会输入最后一个。这就像“ExpenseActions.tsx”组件从表中获取所有数据,而不仅仅是从与之对应的行中获取数据。当我单击第一行的“修改”按钮时,模态应该获取“项目 1”的数据,但模态显示“项目 3”行的值,依此类推。
评论