Vue: Vue always rerenders child components passed through a default slot

Created on 29 May 2018  ·  3Comments  ·  Source: vuejs/vue

Version

2.5.2

Reproduction link

https://codesandbox.io/s/10z84qw33q

Steps to reproduce

Open the sample.

Here there are two simple components: Row and Cell.

We iterate through an array and render a Row for each array item:

{this.items.map(item => {
  return (
    <Row>
      {item.map(message => {
        return <Cell message={message} />;
      })}
    </Row>
  );
})}

Row just renders a Cells passed as a default slot:

render() {
  return <div>{this.$slots.default}</div>;
}

Let's add a new item to the initial array. Click on the 'Add' button to do this.
As you can see in browser console, each 'cell' was rerendered after adding a new one.

At the same time, the similar React sample renders only new cells without rerendering the old ones.

What is expected?

Only new child components should be rendered.

What is actually happening?

All children are rerendered.

Most helpful comment

thanks for that @sqal
It also works with raw render functions:

return h('div', [
      h('button', {
        on: {
          click: () => {
            this.items.push([5, 6]);
          }
        }
      }, 'Add'),
      ...this.items.map(item => h(Row, {}, [
        ...item.map(message => h(Cell, { props: { message }}))
      ]))
    ])

All 3 comments

This should be a problem of babel-plugin-transform-vue-jsx. It don't rerender when using template. https://codesandbox.io/s/0ovk0wj8ql

I think @liximomo is right. This is the same problem as https://github.com/vuejs/vue/issues/7300 and @yyx990803 explained it in his comment:

You can see here https://jsx.egoist.moe/ how your code looks like after transpiling to plain JS:

item.map(message => {
  return h(
    Cell,
    {
      attrs: {
        message: message
      }
    },
    [] // <---- this empty array is causing unnecessary re-render
  );
})

// edit: This was actually fixed some time ago in commit https://github.com/vuejs/babel-plugin-transform-vue-jsx/commit/e5dcbb664472f2a93ddfd78697eefed41b68e439 and was published in version 3.5.1 but CodeSanbox uses version 3.5.0 of the plugin :D So you should just upgrade dependencies in your project.

thanks for that @sqal
It also works with raw render functions:

return h('div', [
      h('button', {
        on: {
          click: () => {
            this.items.push([5, 6]);
          }
        }
      }, 'Add'),
      ...this.items.map(item => h(Row, {}, [
        ...item.map(message => h(Cell, { props: { message }}))
      ]))
    ])
Was this page helpful?
0 / 5 - 0 ratings