vitejs / vite-plugin-vue

Vite Vue Plugins
MIT License
493 stars 154 forks source link

Missing key in JSX when rendering conditionally unlike v-if #304

Open Maxim-Mazurok opened 11 months ago

Maxim-Mazurok commented 11 months ago

Related plugins

Describe the bug

My colleague @tongxuanbao and I found an interesting issue when using JSX in Vue 3 for conditional rendering.

The symptom was that having this code:

<div>
    {option === 1 && <MyCounter myprop="1" />}
    {option === 2 && <MyCounter myprop="2" />}
</div>

would result in MyCounter preserving its internal state when option changes. It would receive updated prop as expected, however internal data/ref state inside of the component would not change, and when option changes there would be no unmounting and mounting.

The core issue stems from not having a key like <MyCounter key="1" myprop="1" />. We have inspected the compiled JavaScript code of vanilla Vue 3 using v-if with the compiled code of the latest Vue JSX app and the core difference was that vanilla Vue was adding key=0 and key=1 automatically, while the JSX plugin was not.

Here's the compiled vanilla Vue code:

t.value == 0 ? (ve(), gn(Xe(ar), { key: 0, label: "label1" })) : Xs("", !0),
t.value == 1 ? (ve(), gn(Xe(ar), { key: 1, label: "label2" })) : Xs("", !0),

And here's the compiled JSX code:

e.value === 0 && U(cr, { label: "label1" }, null),
e.value === 1 && U(cr, { label: "label2" }, null),

image

Reproduction

https://stackblitz.com/edit/vitejs-vite-ygezuc?file=src%2FApp.jsx

Steps to reproduce

  1. npm install && npm run dev
  2. Click on a button to increase counter 3 times
  3. Change the option from 0 to 1
  4. Expect to see internal counter state to be 0, but actually it will remain the same - 3; At the same time, props are updated so it says "Counter 1"

System Info

System:
    OS: Linux 5.0 undefined
    CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 0 Bytes / 0 Bytes
    Shell: 1.0 - /bin/jsh
  Binaries:
    Node: 18.18.0 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 9.4.2 - /usr/local/bin/npm
    pnpm: 8.10.5 - /usr/local/bin/pnpm
  npmPackages:
    @vitejs/plugin-vue: ^4.5.0 => 4.5.0 
    @vitejs/plugin-vue-jsx: ^3.1.0 => 3.1.0 
    vite: ^5.0.2 => 5.0.2

Used Package Manager

npm

Logs

No response

Validations

Maxim-Mazurok commented 11 months ago

I believe this is related to https://github.com/vuejs/core/issues/1587

In which this case was handled in the vue compiler core: https://github.com/vuejs/core/blob/2cece5ba1b9b254cface23096d17ed0e1910467d/packages/compiler-core/src/transforms/vIf.ts#L46-L57

edison1105 commented 1 month ago

a workaround

{option.value === 0 && <MyCounter key={0} label={'Counter 0'}></MyCounter>}
{option.value === 1 && <MyCounter key={1} label={'Counter 1'}></MyCounter>}