Home  >  Q&A  >  body text

Vue 3 eliminates the reactivity of component props

I have EditTransaction component and call it like this:

<edit-transaction
    v-if="editableTransaction.id === transaction.id"
    :key="'transaction'+transaction.id+etcount"
    :class="{'bg-red-300': type === 'expense', 'bg-green-300': type === 'income'}"
    :groups-prop="modelValue"
    :transaction="transaction"
    class="planning-transactions-item px-10 rounded-2xl w-[90%]"
    @close="editableTransaction = {id: null}">
</edit-transaction>

As you can see, I am sending a transaction object in it. Since this is an editor, I don't want the transaction object to be reactive. If someone closes the editor, I want the original transaction object not the modified transaction object, so if I'm correct and want to remove the proxy, I'd put it in the editor:

const form = toRaw(props.transaction)

Inside the editor template, there are some asset components whose v-model values ​​are bound to the form object

<div class="flex gap-5 w-full">
    <FormInput id="et-date" v-model="form.due_date" class="et-fields tw-fields w-[150px]"
               placeholder="Date"
               type="date"
               @keyup.enter="saveChanges"></FormInput>
    <FormInput id="et-name" v-model="form.name" class="et-fields tw-fields" placeholder="Name"
               @keyup.enter="saveChanges"></FormInput>
    <FormInput id="et-value" v-model="form.value" class="et-fields tw-fields" mask-thousand
               placeholder="Value"
               @keyup.enter="saveChanges"></FormInput>
</div>

The problem is that when I change the transaction name, the form object changes and also the transaction properties change. Therefore, the names in the parent data also change because transaction properties are reactive. What am I doing wrong or how can I implement a form object whose values ​​are populated on component creation using props values ​​and without any delegate?

P粉337385922P粉337385922211 days ago322

reply all(2)I'll reply

  • P粉950128819

    P粉9501288192024-03-22 09:25:51

    It's common to use props to pass an initial value to the state of a child component. This means that you "copied" the value of a prop in your local data. It ensures that prop values ​​are protected from unexpected changes: Read more in Vue Documentation

    Here is a very simple example demonstrating the above approach:

    /your-child-component-vue/

    export default {
      props: ['initialCounter'],
      data() {
        return {
          // counter only uses this.initialCounter as the initial value;
          // it is disconnected from future prop updates.
          counter: this.initialCounter
        }
      }
    }
    

    Now, reading your example, I see that you are trying to update some data in the form, and you don't want to change the initial information unless confirmed by a button or something. The process to solve this problem is:

    • Pass initial data that may be changed by the user as a prop.
    • If the user changes some data via input elements, but does not confirm those changes (via buttons), leave the data unchanged (this means you do not issue any changes to the parent element, leaving the prop value unchanged)
    • If the user changes some data and confirms it, send the updated data to the parent (this.$emit) so that it understands the changes.

    reply
    0
  • P粉573943755

    P粉5739437552024-03-22 00:34:17

    So I found two solutions:

    const form = reactive({...props.transaction})

    or

    const form = Object.assign({}, props.transaction)

    Both work, when I change the form value it doesn't change the props.

    reply
    0
  • Cancelreply