7

Still a little bit young in VueJS but I'm loving every bit of it. But now, fixated somewhere.
I want to initialize some values in data() using values passed via props. This is so that I can be able to mutate them later on, since it is not recommended to mutate props inside a component. In fact the official docs recommend this property initialization using prop values as shown below:

{
props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}

I have something like the one below:

<template>
<div class="well">
    <!-- Use Prop value directly on the template: works (but of no help in initializing data) -->
    Department: {{department.name}}
    <!-- Use prop value but gotten via computed property: Works inside the template but not in the initialization -->
    Department: {{fetchDepartment.name}}
    <!-- Use the array I initialized with the prop value: Does not work -->
    Department: {{this_department.name}}
</div>
</template>

<script>
    export default {
        name: 'test',
        props: ['department'],
        data() {
            return {
                this_department: this.department
                // below does not work either
                //this_department: this.fetchDepartment
            }
        },
        created() {
            // shows empty array
            console.log(this.department)
        },
        mounted() {
            // shows empty array
            console.log(this.department)
        },
        computed: {
            fetchDepartment() {
                return this.department
            }
        }
    }
</script>

As seen in the commented sections above, the initialization is not successful. Neither does the value of this.department appear either from the created() or the mounted() hooks. And note, I can see it is defined using the Chrome Vue Devtools. So my question is, how exactly should I initialize data() attributes using props values, or which is the best way of going around this issue?

tony19
  • 125,647
  • 18
  • 229
  • 307
gthuo
  • 2,376
  • 5
  • 23
  • 30
  • Is `department` populated asynchronously? What you are doing is correct. But it if is async, then the initialized value will be null and will not be updated. In which case, the computed is the correct approach. – Bert Aug 29 '17 at 16:09
  • ` `, where `department` is well defined. Not sure exactly what you mean by asychronous though – gthuo Aug 29 '17 at 16:13
  • Do you get the value from an API? – Bert Aug 29 '17 at 16:13
  • Well, the parent component gets the component from API and (I guess) by the time it is loading this component the value is already loaded – gthuo Aug 29 '17 at 16:15
  • That's not necessarily a good assumption. Unless you specifically set up the parent to render *after* the API call is complete, then the parent will render with empty values *first*, then it will update with the values from the API. – Bert Aug 29 '17 at 16:17
  • 1
    Good hint. Let me check it up and I will be back :-) – gthuo Aug 29 '17 at 16:18
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/153152/discussion-between-bert-and-george-thuo). – Bert Aug 29 '17 at 16:27

4 Answers4

3

I know my answer comes in late but it helps me and hopefully someone else coming here. When props' data are async:

// in the parent component
<template>
    <child :foo="bar" v-if="bar" />
</template>

That way, you render the component when props are already available making it safer to follow the guide's recommended ways to initialize data value with props as so:

props: ['initialCounter'],
data: function () {
    return {
       counter: this.initialCounter
    }
}

Happy coding!

igreka
  • 330
  • 3
  • 15
1

You CAN modify a prop. Use the '.sync' modifier. I use it frequently because it is convenient and intuitive. This requires emitting an event to update the value on the parent. I am not really sure the warning of how it results in maintenance issues.

Another method I use if I want to modify a value and not update the parent is using Lodash clone. For example (assuming its available on mounted)

mounted(){
    this_department = _.clone(this.department)
}

If you consistently want to mutate the prop and have it change with the parent, then use a computed property. However, in most cases you will want to depend on the state of that data within the component and change it using other functions and thus a computed property will not be what you need.

tony19
  • 125,647
  • 18
  • 229
  • 307
For the Name
  • 2,459
  • 18
  • 17
  • This doesn't handle the initial render. – Michael Cole Mar 26 '19 at 16:15
  • @MichaelCole With modifying a prop, on initial render you will be receiving the prop data from the parent component. You can, of course, modify it in mounted or created if you choose or change it on the parent component. – For the Name Mar 28 '19 at 02:48
0

This answer is coming late but for someone using vue3 composition API and the eslint-plugin-vue it is not a good practice to apply the props directly into a reference like so :

<script setup>
import { ref } from 'vue'
const props = defineProps(['prop'])
const initialValue = ref(props.prop)
</script>

Eslint will throw an error : Getting a value from the props in root scope of <script setup> will cause the value to lose reactivity vue/no-setup-props-destructure

(Here an issue on the eslint-plugin-vue repo to learn more about it)

The right way to do so and to keep reactivity between the props and the value is to use a watch like explained on the eslint-plugin-vue documentation

Example :

<script setup>
import { ref, watch } from 'vue'

const props = defineProps(['prop'])
const initialValue = ref(null)

watch(() => props.prop, (v) => {
  initialValue.value = v
}, { immediate: true })
</script>
kravorkid
  • 11
  • 2
-1

A computed property is the simplest way to provide a mutable version of a prop, but you might not want to lose data when the prop is updated. You could use an explicit watch.

Watchers

While computed properties are more appropriate in most cases, there are times when a custom watcher is necessary. That’s why Vue provides a more generic way to react to data changes through the watch option. This is most useful when you want to perform asynchronous or expensive operations in response to changing data.

This is most useful when you want to perform asynchronous or expensive operations in response to changing data.