'Only send values that have changed in formik onSubmit

I have a small table of data that is pre-filled by an api call on page load. Each row in the table has a facility name and an Enabled value which comes from the api call. If Enabled == true the checkbox is shown as checked.

I have a couple checkboxes that are grayed out because of a condition in which it cannot be changed at all so there's a readOnly attribute on the field.

Onto my question, for brevity I condensed the list of facilities shown in the table, but in reality there could be X amount of facilities. In my onSubmit function I only want to send the facility name and enabled value if they have been selected/deselected instead of sending the entire list every time submit is pressed.

Ideally I want the shape of the data sent to look like so:

{
    facilityName: "Backup Restore",
    enabled: true
}

Now, I have been able to isolate JUST the facilityName and enabled value but I cannot figure out to only include the values that have been changed in the form. Like I said above, not every single facility name and enabled key/value should be sent every time submit is pressed just the ones that have been changed.

This is what I currently have(and can be seen in the sandbox below) in my onSubmit function

         <Formik
          enableReinitialize
          initialValues={{
            facilities: loggingFacilities
          }}
          onSubmit={async (values, { setSubmitting }) => {

            alert(JSON.stringify(values, null, 2));
            try {

              setLoggingFacilities(values.facilities);
              const valuesToSend = values.facilities.map(facility => {
                return {
                  key: facility.facilityName,
                  value: facility.enabled
                };
              });
              .....

I have a codesandbox here



Solution 1:[1]

From the second param of your onSubmit callback, you can access props, so you could diff values against props.initialValues or however you have called the prop you use to initialize the form.

Actually, that's the suggestion from Jared Palmer:

You can compare initialValues and values within handleSubmit / onSubmit.

Using Array.prototype.filter() it would look something like this:

onSubmit={async (values, { props, setSubmitting }) => {

  // Let's assume this has the same format as `values.facilities`:
  const { initialValues } = props;

  try {
    setLoggingFacilities(values.facilities);

    const valuesToSend = values.facilities.filter((facility, i) => {
      // Let's send only those that have just been disabled or enabled:
      return facility.enabled !== initialValues[i].enabled;
    }).map((facility) => ({
      key: facility.facilityName,
      value: facility.enabled
    }));

    // ...

  } catch(err) { ... }

}

Solution 2:[2]

Try this:

const getChangedValues = (values, initialValues) => {
  return Object
    .entries(values)
    .reduce((acc, [key, value]) => {
      const hasChanged = initialValues[key] !== value

      if (hasChanged) {
        acc[key] = value
      }

      return acc
    }, {})
}

Solution 3:[3]

I'm arriving later to this issue but I think you can use dirty flag. It appears to do the deep comparing with initial values for you. And will complement nicely with @Dazinger approach.

dirty: boolean Returns true if values are not deeply equal from initial values, false otherwise. dirty is a readonly computed property and should not be mutated directly.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 randomKek
Solution 3 Guillermo F. Lopez