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,
};
};