I’m developing a React application with a tabbed interface where users can edit program parameters. The data is not saved to the database until the user explicitly clicks the “Save Changes” button. I am using a custom edit mode to allow users to make changes in the frontend.
Problem:
When users switch between tabs and return to a tab where they have made edits, the changes are not reflected, and the original data is displayed instead of the updated data.
ParameterPage Component:
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useFetchProgramParametersData } from '@/api/querys';
import ProgramParameters from '@/components/ProgramSpecialParametersTab/ProgramParameters';
import SpecialParameters from '@/components/ProgramSpecialParametersTab/SpecialParameters';
import { useAppDispatch, useAppSelector } from '@/Hooks/hooks';
import { toggleEditMode } from '@/features/EditMode/editModeSlice';
const ParameterPage = () => {
let { ProgramID } = useParams();
const dispatch = useAppDispatch();
const { isEditModeActiv } = useAppSelector((state) => state.editModeState);
const { data, isError, isLoading } = useFetchProgramParametersData(ProgramID as string);
const [editableData, setEditableData] = useState<any>(data?.programData);
useEffect(() => {
if (data?.programData) {
setEditableData(data.programData);
}
}, [data]);
const handleToggleEditMode = (event: any) => {
const buttonType = (event.currentTarget as HTMLButtonElement).dataset
.buttonType;
if (isEditModeActiv) {
if (buttonType === 'discardChanges') {
// Handle discard changes
} else if (buttonType === 'saveChanges') {
// Handle save changes
}
}
dispatch(toggleEditMode());
};
if (!ProgramID) return <div>Error: ProgramID is required</div>;
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error loading data</div>;
return (
<div>
<Tabs defaultValue='Overview'>
<TabsList>
<TabsTrigger value='Overview'>Overview</TabsTrigger>
<TabsTrigger value='ProgramParameters'>Program Parameters</TabsTrigger>
<TabsTrigger value='SpecialParameters'>Special Parameters</TabsTrigger>
</TabsList>
<TabsContent value='Overview'>Overview Content</TabsContent>
<TabsContent value='ProgramParameters'>
<ProgramParameters ProgramData={editableData} />
</TabsContent>
<TabsContent value='SpecialParameters'>
<SpecialParameters ProgramData={editableData} />
</TabsContent>
</Tabs>
</div>
);
};
export default ParameterPage;
ProgramParameter Component:
import { ParameterList } from '@/utils/type'
import { useTranslation } from 'react-i18next'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'
import { Info, Pencil } from 'lucide-react'
import { getRowClassName } from '@/utils/getRowClassName'
import { useAppSelector } from '@/Hooks/hooks'
import { RootState } from '@/store'
import { filterParameters } from './filterParameters'
import { useState } from 'react'
import EditProgramSpecialDailog from './EditProgramSpecialDailog'
import { Button } from '../ui/button'
import { useParams } from 'react-router-dom'
type ProgramParametersProps = {
ProgramData: ParameterList
}
const ProgramDataTable = ({ ProgramData }: { ProgramData: ParameterList }) => {
const { t } = useTranslation()
const { parameters } = useAppSelector(
(state: RootState) => state.defaultValues
)
let { ProgramID } = useParams()
const [filterdParameters, setFilterdParameters] = useState<ParameterList>(
filterParameters(ProgramData, parameters, 'PRG', ProgramID as string)
)
const [EditDialog, setEditDialog] = useState<boolean>(false)
const [selectedItem, setSelectedItem] = useState<any>(null)
const { isEditModeActiv } = useAppSelector(
(state: RootState) => state.editModeState
)
const openEditDialog = (item: any) => {
setSelectedItem(item)
setEditDialog(true)
}
return (
<div className='col-span-4 flex flex-col rounded-xl border overflow-auto max-h-[50vh]'>
<Table className='w-full'>
<TableHeader className='sticky top-0 bg-popover'>
<TableRow>
<TableHead>{t('T_Description')}</TableHead>
<TableHead>{t('T_Value')}</TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody className='p-0 overflow-scroll'>
{filterdParameters?.map((item, index) => (
<TableRow key={index} className={getRowClassName(index)}>
<TableCell>
<div className='flex'>
<div>{t(`T_${item.ParameterName}`)}</div>
</div>
</TableCell>
<TableCell className=''>
<div className='flex gap-4'>
<div className='flex items-center'>
{item.Value ? item.Value : 'N/D'}{' '}
{item.Value && item.ParameterUnit}{' '}
</div>
</div>
</TableCell>
<TableCell className='p-0'>
{isEditModeActiv ? (
<Button
variant='link'
onClick={() => {
openEditDialog(item)
}}
>
<Pencil size={20} />
</Button>
) : (
<Button variant='link' className=' cursor-default'>
<Info size={20} />
</Button>
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{EditDialog && (
<EditProgramSpecialDailog
EditDialog={EditDialog}
setEditDialog={setEditDialog}
selectedItem={selectedItem}
setFilterdParameters={setFilterdParameters}
/>
)}
</div>
)
}
const ProgramParameters = ({ ProgramData }: ProgramParametersProps) => {
return (
<div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-8'>
<ProgramDataTable ProgramData={ProgramData} />
</div>
)
}
export default ProgramParameters
SpecialParameter Component:
import { ParameterList } from '@/utils/type'
import { useTranslation } from 'react-i18next'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'
import { Info, Pencil } from 'lucide-react'
import { getRowClassName } from '@/utils/getRowClassName'
import { filterParameters } from './filterParameters'
import { RootState } from '@/store'
import { useAppSelector } from '@/Hooks/hooks'
import { useState } from 'react'
import EditProgramSpecialDailog from './EditProgramSpecialDailog'
import { Button } from '../ui/button'
import { useParams } from 'react-router-dom'
type SpecialParametersProps = {
ProgramData: ParameterList
}
const SpecialDataTabel = ({ ProgramData }: { ProgramData: ParameterList }) => {
const { t } = useTranslation()
const { parameters } = useAppSelector(
(state: RootState) => state.defaultValues
)
let { ProgramID } = useParams()
const [filterdParameters, setFilterdParameters] = useState<ParameterList>(
filterParameters(ProgramData, parameters, 'Special', ProgramID as string)
)
const [EditDialog, setEditDialog] = useState<boolean>(false)
const [selectedItem, setSelectedItem] = useState<any>(null)
const { isEditModeActiv } = useAppSelector(
(state: RootState) => state.editModeState
)
const openEditDialog = (item: any) => {
setSelectedItem(item)
setEditDialog(true)
}
return (
<div className='col-span-4 flex flex-col rounded-xl border overflow-auto max-h-[50vh]'>
<Table className='w-full'>
<TableHeader className='sticky top-0 bg-popover'>
<TableRow>
<TableHead>{t('T_Description')}</TableHead>
<TableHead>{t('T_Value')}</TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody className='p-0 overflow-scroll'>
{filterdParameters.map((item, index) => (
<TableRow key={index} className={getRowClassName(index)}>
<TableCell>
<div className='flex'>
<div>{t(`T_${item.ParameterName}`)}</div>
</div>
</TableCell>
<TableCell className=''>
<div className='flex gap-4'>
<div className='flex items-center'>
{item.Value ? item.Value : 'N/D'}{' '}
{item.Value && item.ParameterUnit}{' '}
</div>
</div>
</TableCell>
<TableCell className='p-0'>
{isEditModeActiv ? (
<Button
variant='link'
onClick={() => {
openEditDialog(item)
}}
>
<Pencil size={20} />
</Button>
) : (
<Button variant='link' className=' cursor-default '>
<Info size={20} />
</Button>
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{EditDialog && (
<EditProgramSpecialDailog
EditDialog={EditDialog}
setEditDialog={setEditDialog}
selectedItem={selectedItem}
setFilterdParameters={setFilterdParameters}
/>
)}
</div>
)
}
const SpecialParameters = ({ ProgramData }: SpecialParametersProps) => {
return (
<div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-8'>
<SpecialDataTabel ProgramData={ProgramData} />
</div>
)
}
export default SpecialParameters
How can I ensure that changes made in edit mode are preserved and correctly reflected when switching tabs?
Are there any common patterns or best practices for handling such scenarios in React with tabbed interfaces and edit modes?
Thank you for any help or suggestions!