Skip to content

initial-values has issues with proxies, vee-validate should use toRaw on initial-values prop #4597

@lk77

Description

@lk77

What happened?

Hello,

If a proxy object is passed to :initial-values, it cause issues with the setInPath function,

/**
 * Sets a nested property value in a path, creates the path properties if it doesn't exist
 */
function setInPath(object, path, value) {
    if (isNotNestedPath(path)) {
        object[cleanupNonNestedPath(path)] = value;
        return;
    }
    const keys = path.split(/\.|\[(\d+)\]/).filter(Boolean);
    let acc = object;
    for (let i = 0; i < keys.length; i++) {
        // Last key, set it
        if (i === keys.length - 1) {

            // Here if acc is a proxy object, a warning will be displayed in the console

            acc[keys[i]] = value;
            return;
        }
        // Key does not exist, create a container for it
        if (!(keys[i] in acc) || isNullOrUndefined(acc[keys[i]])) {
            // container can be either an object or an array depending on the next key if it exists
            acc[keys[i]] = isIndex(keys[i + 1]) ? [] : {};
        }
        acc = acc[keys[i]];
    }
}

this warning shows in the console, every time setInPath is used by FieldArray insert method for example

[Vue warn]: Detected a possible deep change on field `value` ref, for nested changes please either set the entire ref value or use `setValue` or `handleChange`,

Reproduction steps

use options api, i don't know if this issue is present in composition api

1 / Add a Form
2 / Add a FieldArray with a Field inside
3 / Add your initial values in the data of the component
4 / pass initialValues to the Form
5 / See the warning in the console when calling insert method of FieldArray
6 / Use markRaw on the initialValue to see that it solves the warning

Version

Vue.js 3.x and vee-validate 4.x

What browsers are you seeing the problem on?

  • Firefox
  • Chrome
  • Safari
  • Microsoft Edge

Relevant log output

runtime-core.esm-bundler.js:41 [Vue warn]: Detected a possible deep change on field `value` ref, for nested changes please either set the entire ref value or use `setValue` or `handleChange`. 
  at <Transition mode="out-in" name="fade" > 
  at *** > 
  at <BaseTransition appear=false persisted=false mode=undefined  ... > 
  at <Transition> 
  at ****
  at <App> 
  at <App>
warn @ runtime-core.esm-bundler.js:41
deep @ vee-validate.esm.js:1711
callWithErrorHandling @ runtime-core.esm-bundler.js:158
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:166
job @ runtime-core.esm-bundler.js:1831
flushPreFlushCbs @ runtime-core.esm-bundler.js:309
updateComponentPreRender @ runtime-core.esm-bundler.js:5916
componentUpdateFn @ runtime-core.esm-bundler.js:5834
run @ reactivity.esm-bundler.js:178
instance.update @ runtime-core.esm-bundler.js:5898
updateComponent @ runtime-core.esm-bundler.js:5725
processComponent @ runtime-core.esm-bundler.js:5660
patch @ runtime-core.esm-bundler.js:5124
patchBlockChildren @ runtime-core.esm-bundler.js:5511
patchElement @ runtime-core.esm-bundler.js:5403
processElement @ runtime-core.esm-bundler.js:5251
patch @ runtime-core.esm-bundler.js:5112
patchBlockChildren @ runtime-core.esm-bundler.js:5511
processFragment @ runtime-core.esm-bundler.js:5597
patch @ runtime-core.esm-bundler.js:5098
patchUnkeyedChildren @ runtime-core.esm-bundler.js:6005
patchChildren @ runtime-core.esm-bundler.js:5939
processFragment @ runtime-core.esm-bundler.js:5623
patch @ runtime-core.esm-bundler.js:5098
patchBlockChildren @ runtime-core.esm-bundler.js:5511
patchElement @ runtime-core.esm-bundler.js:5403
processElement @ runtime-core.esm-bundler.js:5251
patch @ runtime-core.esm-bundler.js:5112
componentUpdateFn @ runtime-core.esm-bundler.js:5857
run @ reactivity.esm-bundler.js:178
instance.update @ runtime-core.esm-bundler.js:5898
callWithErrorHandling @ runtime-core.esm-bundler.js:158
flushJobs @ runtime-core.esm-bundler.js:362
Promise.then (async)
queueFlush @ runtime-core.esm-bundler.js:275
queueJob @ runtime-core.esm-bundler.js:269
scheduler @ runtime-core.esm-bundler.js:1853
triggerEffect @ reactivity.esm-bundler.js:373
triggerEffects @ reactivity.esm-bundler.js:363
triggerRefValue @ reactivity.esm-bundler.js:966
eval @ reactivity.esm-bundler.js:1123
triggerEffect @ reactivity.esm-bundler.js:373
triggerEffects @ reactivity.esm-bundler.js:358
triggerRefValue @ reactivity.esm-bundler.js:966
eval @ reactivity.esm-bundler.js:1123
triggerEffect @ reactivity.esm-bundler.js:373
triggerEffects @ reactivity.esm-bundler.js:358
trigger @ reactivity.esm-bundler.js:335
set @ reactivity.esm-bundler.js:489
setInPath @ vee-validate.esm.js:371
insert @ vee-validate.esm.js:3299
checkInsert @ ***
eval @ ****
fn._withMods.fn._withMods @ runtime-dom.esm-bundler.js:1396
callWithErrorHandling @ runtime-core.esm-bundler.js:158
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:166
emit @ runtime-core.esm-bundler.js:669
eval @ ****
Promise.then (async)
nextTick @ runtime-core.esm-bundler.js:242
clickButton @ ****
onClick @ *****
callWithErrorHandling @ runtime-core.esm-bundler.js:158
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:166
invoker @ runtime-dom.esm-bundler.js:595

Demo link

github.com/lk77/test-vee-validate-bug

<template>
    <Form :initial-values="initialValues">
        <FieldArray name="locations" v-slot="{ fields, push, remove, insert }">
            <div v-for="(entry, idx) in fields" :key="entry.key">
                <Field :name="`locations[${idx}]`" v-slot="{ value, handleChange }">
                    <input :value="value.latitude"/>
                    <input :value="value.longitude"/>
                </Field>
                <button type="button" @click="remove(idx)">Remove</button>
                <button type="button" @click="insert(idx, {latitude: null, longitude: null})">Insert</button>
            </div>
            <button type="button" @click="push('')">Add</button>
        </FieldArray>
        <button>Submit</button>
    </Form>
</template>

<script>
    import {Form, FieldArray, Field} from "vee-validate";

    export default {
        name: 'Bug',
        components: {Form, FieldArray, Field},
        data() {
            return {
                initialValues: {
                    locations: [
                        {
                            latitude: null,
                            longitude: null
                        }
                    ]
                }
            }
        }
    }
</script>

<style scoped>

</style>

Screenshot from 2023-12-14 11-42-14
Screenshot from 2023-12-14 11-42-35

the only way to fix this issue is to use markRaw on the initial values like so :

initialValues: markRaw({
    locations: [
        {
            latitude: null,
            longitude: null
        }
    ]
})

Edit : for some reason, using markRaw on the initial values prevent the value to change when calling handleChange

Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions