Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue 2.0 infinite update loop when using functional component props as a props for its root element #3276

Closed
OEvgeny opened this issue Jul 14, 2016 · 8 comments

Comments

@OEvgeny
Copy link

OEvgeny commented Jul 14, 2016

Vue.js version

2.0.0-beta.1

Reproduction Link

http://codepen.io/OEvgeny/pen/qNpBqX?editors=1010

Steps to reproduce

Open console devtools to see error message

What is Expected?

HelloWorld component renders div with passed down style without warning.

What is actually happening?

I've got warning from Vue:

 [Vue warn]: You may have an infinite update loop for watcher with expression "function () {
        vm._update(vm._render(), hydrating);
      }" (found in root instance)
@OEvgeny OEvgeny changed the title Vue 2.0 infinite update loop when using functional component props as a props for component root element Vue 2.0 infinite update loop when using functional component props as a props for its root element Jul 14, 2016
@kazupon
Copy link
Member

kazupon commented Jul 14, 2016

updates.count++ in render function is occured rendering loop.

the below rendering flow:

- render root 
- render child  (HelloWorld)
- root watcher detect props changing
- render root 
- render child 
- root watcher detect props changing
- ...

I think that render function of functional component is updating the parent props is bad practice in Vue 2.0

@yyx990803 If you have my mistake comment or any comments, please tell us.

@kazupon kazupon added the 2.0 label Jul 14, 2016
@OEvgeny
Copy link
Author

OEvgeny commented Jul 14, 2016

@kazupon Sorry, counter was here for showing purposes only. I didn't realize that it will affect to rendering flow of child component.
So, updated repro: http://codepen.io/OEvgeny/pen/qNpBqX?editors=1010
Warning message is equal with previous:

[Vue warn]: You may have an infinite update loop for watcher with expression "function () {
        vm._update(vm._render(), hydrating);
      }" (found in root instance)

@kazupon
Copy link
Member

kazupon commented Jul 14, 2016

Thanks!!

I found your mistake at render function.

components: {
    HelloWorld: {
      props: ['params'],
      functional: true,
      render (h, ctx) {
        console.log(ctx.props.params.style.background)
        //return h('div', ctx.props.params, ctx.children)
        return h('div', ctx.data, ctx.children) // correct usage
      }
    }
  }

About h, See the more detail:
https://github.com/vuejs/vue/releases/tag/v2.0.0-alpha.5

@OEvgeny
Copy link
Author

OEvgeny commented Jul 14, 2016

@kazupon Hmm ctx.data contains only raw attributes, I need to pass to h function component property called params as a tag props.

@kazupon
Copy link
Member

kazupon commented Jul 14, 2016

Okay.
In this case, You need to pass '{ props: ctx.props }' to 'h' function.

@OEvgeny
Copy link
Author

OEvgeny commented Jul 14, 2016

@kazupon No, again. By props i meant tag attributes not component properties. Sorry for mistake.
Provided repro works as expected except warning and extra render calls.

So, params from component properties should be applied as tag attributes. For example for params:

params: {
  style: { backgroundColor: "#ccc" }
}

I need to create node which looks like:

h('div', {style: { backgroundColor: "#ccc" }}, ctx.children)

My component gets params as its property and passes it down to h function as node attributes.

      props: ['params'],
      functional: true,
      render (h, ctx) {
        console.log(ctx.props.params) //{style: { backgroundColor: "#ccc" }}
        return h('div', ctx.props.params, ctx.children)
      }
    }

@yyx990803
Copy link
Member

This is somewhat expected because vnode data should be fresh objects on each render. I've added a warning for using observed objects as vnode data.

To get around this, simply shallow copy the object before sending it in:

render (h, ctx) {
   return h('div', Vue.util.extend({}, ctx.props.params), ctx.children)
}

@OEvgeny
Copy link
Author

OEvgeny commented Jul 15, 2016

@yyx990803 Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants