vuejs / docs

📄 Documentation for Vue 3
https://vuejs.org
Other
2.9k stars 4.34k forks source link

Props default not reactive #687

Closed ArkIv closed 3 years ago

ArkIv commented 3 years ago

The reactivity of the default 'Object' type attribute is lost, or not created.

<comp :test="test" :test2="test2"></comp>
. . . 
data(){
  return{
    test: [{one:1,two:2},{one:3,two:4}]   // Ok
   }
 }
props:{
  test2: {
  type: Object,
  default: () => {
    return [{one:1,two:2},{one:3,two:4}]  // not reactive
    }
 }
}
setup(props){
   isReactive(props.test) - true
  isReactive(props.test2) - false   ???
}

====>

Found nowhere in the documentation . I had to do so, but what should be done correctly?

props:{
  test2: {
  type: Object,
  default: () => {
    return reactive([{one:1,two:2},{one:3,two:4}])  <= reactive
    }
 }
}
setup(props){
  isReactive(props.test) - true
  isReactive(props.test2) - true  !!
}

Data automatically makes components reactive, But somehow illogical, the data are the same Props, But does not make them reactive although props itself is reactive, If I had not installed default, I would not have noticed this and would never have known.

Add to documentation, at least here https://v3.vuejs.org/guide/component-props.html#prop-validation // Object or array defaults must be returned from // a factory function not reactive, use reactive()

@vue/cli 4.5.8

NataliaTepluhina commented 3 years ago

Thank you for creating an issue!

I am not completely sure what is the case where you need a default value of the prop to be reactive. It's only used as a fallback in the case if you didn't pass any value from the parent component, and you're not supposed to mutate a prop (or its default value) from the child component. Could you please explain why do you need prop default value to be reactive?

ArkIv commented 3 years ago

It is very convenient to work with one object without copying it to add property changes, And even in combination with provide (). Generally after default: () => { return reactive ([{xxxx}, {yyy}, {. . .}]) The object, as it were, ceases to be Default, it is used only in this component and only in this. And for another component, Default will be clean and unchanged again, For a component, I can have an object uploaded via ajax as well as by default. And if it is assumed that the props received via data can be reactive, Then why not make it reactive by default as well. Well, or at least mention that it doesn't work that way.

NataliaTepluhina commented 3 years ago

We do not recommend mutating props object in the child component as it breaks Vue unidirectional dataflow 🤔 Changes should happen in the component where you pass a prop and this should efficiently override a default value.

skirtles-code commented 3 years ago

This isn't specific to the default. In general the values of props are not made reactive. They'll only be reactive if the value passed by the parent was already reactive.

The same is true of objects returned by computed properties. The property itself is reactive but the value is not. Again, if the object returned is already reactive then the reactivity is not stripped.

I've put together a little example to demonstrate some of these cases:

https://jsfiddle.net/skirtle/3ty4dkc6/

This shallow reactivity is by design. There is an expectation that such objects shouldn't be mutated and any changes need to be made to upstream reactive data instead.

The object, as it were, ceases to be Default, it is used only in this component and only in this. And for another component, Default will be clean and unchanged again,

That's true. To some extent you could view the current component as the owner for that particular object and argue that it is fine to edit it. However, there are a couple of problems with that way of looking at it:

  1. If the object isn't the default then you shouldn't be editing it as it's owned by the parent component. It's difficult to imagine a scenario where conditionally mutating the object only when it's the default makes sense.
  2. The default could be re-applied when the parent re-renders, discarding any changes made to the original default object. e.g. See this JSFiddle. If you edit the value and then click the button the changes are lost.

However, all that said, there is a legitimate question about what we can add to the documentation to make this clearer. There is a difficult line to tread, including explanations for all the edge cases while not confusing the reader with too much information. Historically, statements like 'methods are not reactive' have led to a lot of confusion and the scenario described here is similar. It's really difficult to explain what such statements really mean without losing the reader.

ArkIv commented 3 years ago

Thanks for the detailed answer. And in the documentation (props), just add that the default will not be not reactive. It may be confusing that we do not specify in Data reactive.

NataliaTepluhina commented 3 years ago

Considering @skirtles-code point about complicated explanation and my own comments about how props default value is used, I don't think we need to add this to documentation. As I've mentioned above, you shouldn't override props default value from the child component (thus you don't need a reactivity), and when you pass a value from the parent component, default will be overridden by the actual value and will be reactive/non-reactive depending on what parent component passes down.