This component checks if the fields have been changed from the initial value and display the number of changed fields. There's a useEffect for each field. It would be better with something more DRY, but haven't figured out how.
Is there a way to do this faster and better?
States
const [unsavedChanges, setUnsavedChanges] = useState<Array<string>>([]);
const [customField1, setCustomField1] = useState<string | null | undefined>(
Data.field1
);
const [customField2, setCustomField2] = useState<string | null | undefined>(
Data.field2
);
Set unsavedChanges state
//Field 1
useEffect(() => {
if (
(customField1 === Data.field1 ||
customField1 === null) &&
unsavedChanges?.includes('customfield1')
) {
let filtered = unsavedChanges.filter(
(item) => item !== 'customfield1'
);
setUnsavedChanges(filtered);
} else if (
customField1 === Data.field1 ||
unsavedChanges?.includes('customfield1')
) {
return;
} else {
setUnsavedChanges([...unsavedChanges, 'customfield1']);
}
}, [customField1]);
//Field 2
useEffect(() => {
if (
(customField2 === Data.field2 ||
customField2 === null) &&
unsavedChanges?.includes('customfield2')
) {
let filtered = unsavedChanges.filter(
(item) => item !== 'customfield2'
);
setUnsavedChanges(filtered);
} else if (
customField2 === Data.field2 ||
unsavedChanges?.includes('customfield2')
) {
return;
} else {
setUnsavedChanges([...unsavedChanges, 'customfield2']);
}
}, [customField2]);
Return
<>
<Input value={customField1} onChange={setCustomField1(e.target.value}/>
<Input value={customField2} onChange={setCustomField2(e.target.value}/>
<h1>{unsavedChanges.length} # of unsaved changes.</h1>
<Button disabled={unsavedChanges.length > 0 ? false : true} label='Submit'/>
</>
CodePudding user response:
i created an example of how to make it simple (in my opinion). I merged the three states into one. this way i can get each one in more dynamic way. then i created on change handler that handles the changes and doing the all if statements (with less code). each input is firing the change handler on change. and it sets the state according to the if statements. this way we can create as many inputs as we want, we just need to pass the right arguments to the change handler and it will take care of the rest for us (and to make sure to include one more key value pair to the state)
this is the dummy data for Data:
const Data = { field1: "abc", field2: "efg" };
the state:
const [generalState, setGeneralState] = useState({
unsavedChanges: [],
customField1: "",
customField2: ""
});
the handler:
const changeTheState = (type, value, field, customField) => {
//setting the values to the state. so we can fetch the values for the inputs and for later use
setGeneralState((prev) => ({ ...prev, [type]: value }));
// i narrowed down the if statements. now its only two.
if (
value === Data[field] &&
generalState.unsavedChanges?.includes(customField)
) {
return setGeneralState((prev) => {
let filtered = prev.unsavedChanges.filter(
(item) => item !== customField
);
return { ...prev, unsavedChanges: filtered };
});
}
if (!generalState.unsavedChanges?.includes(customField)) {
setGeneralState((prev) => ({
...prev,
unsavedChanges: [...prev.unsavedChanges, customField]
}));
}
};
and the jsx:
<div className="App">
<input
value={generalState.customField1}
onChange={(e) => {
changeTheState(
"customField1",
e.target.value,
"field1",
"customField1"
);
}}
/>
<input
value={generalState.customField2}
onChange={(e) => {
changeTheState(
"customField2",
e.target.value,
"field2",
"customField2"
);
}}
/>
<h1>{generalState.unsavedChanges.length} # of unsaved changes.</h1>
<button disabled={generalState.unsavedChanges.length > 0 ? false : true}>
Submit
</button>
</div>
here is the example : codesandbox example
One more thing you can do , is to create a reusable component for the input. create array of objects to represent each input. loop through the array and generate as many inputs you want.
if you need extra explaination let me know.