Closed gagarine closed 3 years ago
A solution is to use watcher, but it seem strange to create a reactive value has we already have one.
import { useField } from 'vee-validate';
import {useSubscriberStore} from "../store/subscriber.js";
setup(props, context) {
const { handleSubmit } = useForm()
const subscriberStore = useSubscriberStore()
// I want to use subscriberStore.firstname here in useField witch is a reactive value already
let { errorMessage: firstnameErrorMessage, value: firstname, name: firstnameName } = useField('firstnameName');
// Apply the store change to the vee-validate value
watchEffect(() => {
firstname.value = subscriberStore.firstName
})
return {errorMessage, subscriberStore.firstName, firstnameName}
}
This has come up a couple of times before, I will explain the caveats here:
While having a watcher may seem the way at first glance you have to remember that vee-validate needs two-way access to that reactive value, because vee-validate doesn't just read values, it also writes them. so the writability of a reactive reference cannot be guaranteed.
To clarify, you may pass reactive values using these methods:
ref
: this is possible to sync because it allows read/writecomputed
: Possible if the user provided a setter, and that cannot be guaranteedtoRef
: creates read-only refs, not writable.shallowRef
: creates writable ref but vee-validate cannot detect changes to nested values which will break the dirty
flagAFAIK there is no way to tell if a reactive ref is writable or not without actually trying.
So instead of electing to provide an API that "sometimes" works, I prefer to leave it to userland. Another justification of that decision is that vee-validate now handles the value tracking aspect, so doing it twice is kind of redundant, depends on your use case of course.
When it comes to stores, I advise against tracking form values using stores. In my eyes, stores are meant for global sharable data, and most forms don't share their state around the entire app. Also more often than not, forms have an ephemeral state (non-persistable).
You can pass a reactive value to useField
using the initialValue
option but it will just clone it, and it won't sync it for those reasons.
Thanks for your reply.
vee-validate doesn't just read values, it also writes them.
Mmmh I didn't catch that in the doc.
When it comes to stores, I advise against tracking form values using stores. In my eyes, stores are meant for global sharable data, and most forms don't share their state around the entire app. Also more often than not, forms have an ephemeral state (non-persistable).
Use case could be multi-step form or form that allow to manipulate an object that you save from time to time. So exactly when you want multiple form to keep and share their state.
I'm coming back to this bug, I still don't understand why you do not accept reactive value.
AFAIK there is no way to tell if a reactive ref is writable or not without actually trying.
This could be documented: "only accept writable reactive value created with ref()". And when you write to the value you can use a try and catch and generate a nice exception that explain that the passed reactive value is not writable.
It's not a bug, this is intended. The behavior you want existed in v4 alpha releases, but was removed due to not being able to cover all grounds which is in my opinion makes it easier to explain and to work with.
I will elaborate more on other issues with allowing this:
Not all values can be set as direct assignments like your case. For example, an array created with a computed
ref without a setter can still be modified with push
and other mutation array methods, but only in some cases. Same issue with objects or non-primitive refs.
So that again brings us to the issue of "an API that sometimes works" and I don't like to have something obscure like that around with so many caveats and edge cases.
try and catch and generate a nice exception that explains...
Except AFAIK Vue doesn't throw, it just warns. So we cannot tell if the value changed or not unless it inspects the value before and after the setter is run. And even then, async setters won't work well with that behavior because they will be detected as "read-only" when they are not.
Thanks for the detailed explanations. Always nice to know why things are like they are.
@gagarine I had similar problem and I found solution without watchEffect - If you want it write me here.
A solution is to use watcher, but it seem strange to create a reactive value has we already have one.
import { useField } from 'vee-validate'; import {useSubscriberStore} from "../store/subscriber.js"; setup(props, context) { const { handleSubmit } = useForm() const subscriberStore = useSubscriberStore() // I want to use subscriberStore.firstname here in useField witch is a reactive value already let { errorMessage: firstnameErrorMessage, value: firstname, name: firstnameName } = useField('firstnameName'); // Apply the store change to the vee-validate value watchEffect(() => { firstname.value = subscriberStore.firstName }) return {errorMessage, subscriberStore.firstName, firstnameName} }
nice solution ! 🙏
@gagarine I had similar problem and I found solution without watchEffect - If you want it write me here.
How did you manage it ?
If you use vuex or pinna as a store, you want to use your store value directly. Those a reactive value. Yet useField() create a reactive value but you cannot pass an existing one to it.
Their is related questions on stack: