How to find out if the user has enter any value in the input field in react.
What I am trying to achieve is I would like to show a border around the card when the user enters the text/integer into one of the input fields inside that cards and does not click on the save button.
So border should be added to the card containing at least one of all the unsaved input fields and also changed input fields.
I am using dynamic form mentioned in the link below.
This is the link of how my dynamic form is structured (Sample Code):
Dynamic form is a form repeater, wherein when I click on the “add a new row” button. the a new card will be added to the react page.
This new card will have a same set of fields with the same default values. when the user changes any of the input field in the card. a border needs to be added to the card. if the user clicks on the save button the input fields should be saved.
When make changes to a individual input fields of a card, that card is indeed get a border, however when I add a new card. the border for the old card with unsaved input fields is removed.
I have been trying to solve this for a week now. I am not using formik or react-hook-form. I am using react-bootstrap library.
I am finding it difficult to change the border of the card as and when the user enters data into the input field.
I am using a ternary statement to check if the fields are changed are not.
I am using a useState hook like this:
const [isFieldChanged, setisFieldChanged] = useState(false);
I am using handleClick function this to set useState variable: const handleClick = () => { setisFieldChanged(true); };
These are all the fields and this is how I have set the default value:
const [inputFields, setInputFields] = useState([ { fieldName: "", label: "", fieldSet: "", cardinality: 0, required: false, defaultValue: "", weight: 0, }, ]);
Below is the code for ternary statement:
<Card id={`${inputField.id}`} className={ isFieldChanged && index !== 0 && (inputFields.length) <= (index + 1) && (inputField.cardinality !== 0 || inputField.fieldName.length !== 0 || inputField.label.length !== 0 || inputField.fieldSet.length !== 0 || inputField.required !== false || inputField.defaultValue.length !== 0) ? " mt-2 border border-warning" : " mt-2 " } >
For each of the form control I am adding onKeyPress to calling the handleClick function:
<Form.Control type="text" placeholder="fieldName" name="fieldName" data-cy="name-text-field" value={inputField.fieldName} onChange={(event) => { handleInputChange(index, event); }} onKeyPress = {(event)=>{ handleClick(index, event); } />
Advertisement
Answer
What you need is two different states – one for saved and one for current data:
const initialState = { firstName: "", lastName: "", age: "" }; const [savedFormData, saveData] = useState(initialState); const [currentFormData, setCurrentFormData] = useState(initialState);
Add an onChange event to your Form.Controls:
const handleChange = (name, value) => { setCurrentFormData((prev) => { return { ...prev, [name]: value }; }); }; ... // in your Form.Control onChange={(event) => handleChange("firstName", event.target.value) }
Define when there is unsafed data: you can compare both states with each other. It’s not sufficient to just say “it’s different when user typed something”, because it can be deleted again, so the states are the same again:
const dataUnsafed = JSON.stringify(savedFormData) !== JSON.stringify(currentFormData);
Finally, on save, you have to copy the current data state to the saved data state and call your API to persit the data:
const onSave = () => { saveData(currentFormData); // Call your API here to persist your "savedFormData };
Use “dataUnsafed” to set styles on your card, here I just set a thicker border:
<Card className={dataUnsafed ? "border-3" : ""}>...</Card>