Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using ref with props

I have a Component A with a nested Component B. Component A loads a list of strings, Component B has a SELECT element, which is populated when Component A is finished loading. The first option of the SELECT should be selected at this moment. After this moment, when other options are selected, Component B should emit update event with selection data, so that Component A 'knows' what option was selected. Since A should control B, this option is passed back to B with props.

In my implementation below, only the initial value of the selection is received by Component B, and when the list is loaded, it is populated successfully, but the selectedOption value is not changed by the controlling property. So, it doesn't work.

What would be a proper way to implement this?

Component A:

<template>
  <component-b :list="list" :selected="selected" @update="onUpdate">
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
  setup: () => {
    const list = ref<string[]>([]);
    const selected = ref('');

    load().then(data => {       // some load function returning a promise
      selected.value = data[0]; // data is never empty
      list.value = data;
    });

    const onUpdate = (data: string) => selected.value = data;

    return { list, selected, onUpdate };
  }
})
</script>

Component B:

<template>
  <select v-model="selectedOption" @change="onChange">
    <option v-for="item of props.list" />{{item}</option>
  </select>
</template>

<script lang="ts">
import { defineComponent, ref, PropType } from 'vue';
export default defineComponent({
  emits: ['update'],
  props: {
    selected: {
      type: String
    },
    list: {
      type: Object as PropType<String[]>
    }
  }
  setup: (props, { emit }) => {
    const selectedOption = ref(props.selected); // this doesn't work when loading is finished and props are updated
    
    const onChange = () => emit('update', selectedOption.value);

    return { selectedOption, onChange, props }
  }
});
</script>

Thank you.

like image 463
Michael Zelensky Avatar asked Jan 22 '26 22:01

Michael Zelensky


1 Answers

In ComponentB.vue, selectedOption's ref is only initialized to the value of props.selected, but that itself is not reactive (i.e., the ref isn't going to track props.selected automatically):

const selectedOption = ref(props.selected); // not reactive to props.selected

You can resolve this with a watchEffect that copies any new values to selectedOption (which happens automatically whenever props.selected changes):

watchEffect(() => selectedOption.value = props.selected);

Side note: setup() doesn't need to explicitly return props because they're already available to the template by name. Your template could be changed to:

<!-- <option v-for="item of props.list">{{ item }}</option> -->

                        👇 reference props directly by name
<option v-for="item of list">{{ item }}</option>

demo

like image 62
tony19 Avatar answered Jan 25 '26 17:01

tony19



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!