最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - infinite update loop in Vue - Stack Overflow

programmeradmin7浏览0评论

I'm trying to write a custom ponent. And hope I can use it like this

let app = new Vue({
    el:'#app',
    template:`
    <tab>
    <tab-item name='1'>
    <h1> This is tab item 1</h1>
    </tab-item>
    <tab-item name='2'>
    <h2> This is tab item 2</h2>
    </tab-item>
    </tab>`,
    ponents:{
        tab,
        tabItem
    }
})

Everything goes fine until you click the button. I got an error from console:

[Vue warn]: You may have an infinite update loop in a ponent render function.

found in

---> <Tab>
       <Root>

I've tried many ways to solve this problem, however, failure always won the debugging petition.

How can I beat this problem?

Here is my code:

let tabItem = {
    props:{
        name:{
            type: String,
            required: true
        }
    },
    render(h){
        let head = this.$slots.head || ''
        let body = this.$slots.default
        let tail = this.$slots.tail || ''
        return h('div', [ 
            h('div', head),
            h('div', body),
            h('div', tail)])
    }
}

let tab = {

    data(){
        return {
            items:'',
            currentView:0
        }
    },
    methods:{
        handleTabClick(item){
            return ()=>{
                let index = this.items.indexOf(item)
                this.currentView = this.items[index]
            }
        },
        extractProps(vnode){
            return vnodeponentOptions.propsData
        }
    },
    render(h){
        this.items = this.$slots.default.filter( node => {
            return /tab-item/.test(node.tag)
        })
        let headers = this.items.map( item => {
            let name = this.extractProps(item).name
            return h('button', {
                on:{
                    click: this.handleTabClick(item)
                }
            }, name)
        })
        let head = h('div', headers)
        this.currentView = this.items[0]
        return h('div',[head, this.currentView])
    }
}

Or any other ways to implement this ponent?

Thanks a lot for helping me out from the hell.

Thanks for your reply my friends. I'm pretty sure that I get an infinite loop error from the console and my code doesn't work as expected. I don't think using vnode is a good way to implement this ponent too. However, this is the best solution I can figure out.

This ponent -- tab should detect its child whose name is tabItem, which is also a ponent. And tab can extract some data from tabItem. In my case, tab will extract the name property of tabItemn, which will be used to generate the buttons for switching content. Click the button can switch to the relevant content, which is the body of tabItem. In my code, it's currenView.

Like a famous UI library, Element, its tab ponent can be used like this:

<el-tabs v-model="activeName" @tab-click="handleClick">
  <el-tab-pane label="User" name="first">User</el-tab-pane>
  <el-tab-pane label="Config" name="second">Config</el-tab-pane>
  <el-tab-pane label="Role" name="third">Role</el-tab-pane>
  <el-tab-pane label="Task" name="fourth">Task</el-tab-pane>
</el-tabs>

I need to implement one ponent like this but mine will be more simple. For learning how to do it, I read its source code. Maybe there's not a good way to filter child ponents. In the source, they use this to filter the el-tab-pane ponent:

  addPanes(item) {
    const index = this.$slots.default.filter(item => {
      return item.elm.nodeType === 1 && /\bel-tab-pane\b/.test(item.elm.className);
    }).indexOf(item.$vnode);
    this.panes.splice(index, 0, item);
  }

Source Code

I know that I can use $children to access its child ponents but doing so doesn't guarantee the order of the child ponents, which is not what I want. Because the order of switching button is important. Detail messages about vnode are not contained in the doc. I need to read the source.

Therefore, after reading the source of Vue, I wrote my code like this then I got my problem.

I finally didn't solve this bug and admit that using this kind of rare code sucks. But I don't know other solutions. So I need you guys help.

Thanks.

I'm trying to write a custom ponent. And hope I can use it like this

let app = new Vue({
    el:'#app',
    template:`
    <tab>
    <tab-item name='1'>
    <h1> This is tab item 1</h1>
    </tab-item>
    <tab-item name='2'>
    <h2> This is tab item 2</h2>
    </tab-item>
    </tab>`,
    ponents:{
        tab,
        tabItem
    }
})

Everything goes fine until you click the button. I got an error from console:

[Vue warn]: You may have an infinite update loop in a ponent render function.

found in

---> <Tab>
       <Root>

I've tried many ways to solve this problem, however, failure always won the debugging petition.

How can I beat this problem?

Here is my code:

let tabItem = {
    props:{
        name:{
            type: String,
            required: true
        }
    },
    render(h){
        let head = this.$slots.head || ''
        let body = this.$slots.default
        let tail = this.$slots.tail || ''
        return h('div', [ 
            h('div', head),
            h('div', body),
            h('div', tail)])
    }
}

let tab = {

    data(){
        return {
            items:'',
            currentView:0
        }
    },
    methods:{
        handleTabClick(item){
            return ()=>{
                let index = this.items.indexOf(item)
                this.currentView = this.items[index]
            }
        },
        extractProps(vnode){
            return vnode.ponentOptions.propsData
        }
    },
    render(h){
        this.items = this.$slots.default.filter( node => {
            return /tab-item/.test(node.tag)
        })
        let headers = this.items.map( item => {
            let name = this.extractProps(item).name
            return h('button', {
                on:{
                    click: this.handleTabClick(item)
                }
            }, name)
        })
        let head = h('div', headers)
        this.currentView = this.items[0]
        return h('div',[head, this.currentView])
    }
}

Or any other ways to implement this ponent?

Thanks a lot for helping me out from the hell.

Thanks for your reply my friends. I'm pretty sure that I get an infinite loop error from the console and my code doesn't work as expected. I don't think using vnode is a good way to implement this ponent too. However, this is the best solution I can figure out.

This ponent -- tab should detect its child whose name is tabItem, which is also a ponent. And tab can extract some data from tabItem. In my case, tab will extract the name property of tabItemn, which will be used to generate the buttons for switching content. Click the button can switch to the relevant content, which is the body of tabItem. In my code, it's currenView.

Like a famous UI library, Element, its tab ponent can be used like this:

<el-tabs v-model="activeName" @tab-click="handleClick">
  <el-tab-pane label="User" name="first">User</el-tab-pane>
  <el-tab-pane label="Config" name="second">Config</el-tab-pane>
  <el-tab-pane label="Role" name="third">Role</el-tab-pane>
  <el-tab-pane label="Task" name="fourth">Task</el-tab-pane>
</el-tabs>

I need to implement one ponent like this but mine will be more simple. For learning how to do it, I read its source code. Maybe there's not a good way to filter child ponents. In the source, they use this to filter the el-tab-pane ponent:

  addPanes(item) {
    const index = this.$slots.default.filter(item => {
      return item.elm.nodeType === 1 && /\bel-tab-pane\b/.test(item.elm.className);
    }).indexOf(item.$vnode);
    this.panes.splice(index, 0, item);
  }

Source Code

I know that I can use $children to access its child ponents but doing so doesn't guarantee the order of the child ponents, which is not what I want. Because the order of switching button is important. Detail messages about vnode are not contained in the doc. I need to read the source.

Therefore, after reading the source of Vue, I wrote my code like this then I got my problem.

I finally didn't solve this bug and admit that using this kind of rare code sucks. But I don't know other solutions. So I need you guys help.

Thanks.

Share Improve this question edited Feb 8, 2018 at 7:09 Tomasz Mularczyk 36.2k19 gold badges118 silver badges174 bronze badges asked Feb 7, 2018 at 14:26 ucagucag 4872 gold badges8 silver badges24 bronze badges 2
  • For sure you have an infinite loop. Your render function always calls itself. What are you trying to do, and why do you need slots, vnode and hand-rolled render functions (which are exotic, potentially plicated and rarely used) ? – bbsimonbb Commented Feb 7, 2018 at 15:08
  • @bbsimonbb yeah, I also think this sucks, but I can't find another way to do it.. I add more information. – ucag Commented Feb 8, 2018 at 6:43
Add a ment  | 

1 Answer 1

Reset to default 6

You shouldn't change your data in render function, this is wrong

this.items = this.$slots.default.filter( node => {
  return /tab-item/.test(node.tag)
})

because it will keep re-rendering, here is a working example for your code, I simply removed items property from data and added new items puted property which returns tab-items nodes.

let tab = {

    data(){
        return {
            currentView:0
        }
    },
    methods:{
        handleTabClick(item){
            return ()=>{
                let index = this.items.indexOf(item)
                this.currentView = this.items[index]
            }
        },
        extractProps(vnode){
            return vnode.ponentOptions.propsData

        }
    },
    puted: {
    	items(){
      	return this.$slots.default.filter( node => {
            return /tab-item/.test(node.tag)
        })
      }
    },
    render(h){
        
        let headers = this.items.map( item => {
            let name = this.extractProps(item).name
            return h('button', {
                on:{
                    click: this.handleTabClick(item)
                }
            }, name)
        })
        
        
        let head = h('div', headers)
        
        this.currentView = this.items[0]
        
        return h('div',[head, this.currentView])
    }
}

let tabItem = {
		name:"tab-item",
    props:{
        name:{
            type: String,
            required: true
        }
    },
    render(h){
        let head = this.$slots.head || ''
        let body = this.$slots.default
        let tail = this.$slots.tail || ''
        return h('div', [[ 
            h('div', head),
            h('div', body),
            h('div', tail)]])
    }
}


let app = new Vue({
    el:'#app',
    template:`
    <tab>
    <tab-item name='1'>
    <h1> This is tab item 1</h1>
    </tab-item>
    <tab-item name='2'>
    <h2> This is tab item 2</h2>
    </tab-item>
    </tab>`,
    ponents:{
        tab,
        tabItem
    }
})
<script src="https://cdn.jsdelivr/npm/[email protected]/dist/vue.min.js"></script>  

<div id="app"></div>

发布评论

评论列表(0)

  1. 暂无评论