Form validity hook in vue 3 composition api

Viewed 71

I'am just writing my first custom hook in vuejs 3. It is intended to act as a bridge between the fields of a form and the outer form component. The fields will use is like this:

const { setValidity } = useFormFieldStore(props.grp, props.id);
...
setValidity(valid);

The form component will then use is like this:

const { valid } = useFormStore(props.grp);

The form will then enable the ok button only if all fields are valid.

I am a little bit a lone worrier within the vue stuff here and I really don't know if this is useful vue-code or not. In concrete I struggled with the following points:

  • I was not able to use directly a Map together with reactive(). Using a map just did not trigger the updates. Therefore I had to serialize and deserialize the map to a string. As this is hidden inside the hook, it does not hurt me too much.
  • I first tried to return a computed instead of the combination of watch() and ref(). But using data.validity inside of computed() did not trigger update events ether.
  • I am generally uncertain if this is a valid way of using/sharing reactivity in vue.

The code is working as intended, but if this is something I should do in vue I don't know. I appreciate any comment or hint on this! Thank you very much!

Best regards, Dominic

import {
  reactive, ref, watch, UnwrapNestedRefs,
} from 'vue';

interface FormStoreData {
  validity: string;
}

const THE_FORM_STORES : Map<string, UnwrapNestedRefs<FormStoreData>> = new Map();

const createFormStoreData = () : UnwrapNestedRefs<FormStoreData> => reactive({
  validity: '{}',
});

const getFormStoreData = (formId : string) : UnwrapNestedRefs<FormStoreData> => {
  let data = THE_FORM_STORES.get(formId);
  if (!data) {
    data = createFormStoreData();
    THE_FORM_STORES.set(formId, data);
  }
  return data;
};

const stringToValidityMap = (value : string): Map<string, boolean> => new Map(Object.entries(JSON.parse(value)));

const validityMapToString = (value : Map<string, boolean>) : string => JSON.stringify(Object.fromEntries(value));

export const useFormStore = (formId : string) => {
  const data = getFormStoreData(formId);

  const valid = ref(true);

  watch(() => data.validity, (newValue) => {
    const valueMap = stringToValidityMap(newValue);
    let result = true;
    valueMap.forEach((v) => {
      if (!v) {
        result = false;
      }
    });
    valid.value = result;
  });

  return {
    valid,
  };
};

export const useFormFieldStore = (formId : string, fieldId : string) => {
  const data = getFormStoreData(formId);

  const setValidity = (value : boolean) => {
    const currentValidityString = data.validity;
    const validityMap = stringToValidityMap(currentValidityString);
    const currentFieldValid = validityMap.get(fieldId);
    if (currentFieldValid) {
      if (currentFieldValid === value) {
        return;
      }
    }
    validityMap.set(fieldId, value);
    const newValidityString = validityMapToString(validityMap);
    if (currentValidityString === newValidityString) {
      return;
    }
    data.validity = newValidityString;
  };

  return {
    setValidity,
  };
};

0 Answers
Related