element-plus: [Bug Report] Cascader `lazyLoad` triggers on every re-render

Element Plus version

1.0.2-beta.51

OS/Browsers version

Windows 10 Chrome v91

Vue version

3.1.2

Reproduction Link

https://github.com/wenfangdu/cascader-lazy-load-repro

Steps to reproduce

Please use the repro link.

What is Expected?

props are checked by content, e.g. using lodash’s isEqual, if it indeed changed, then run lazyLoad.

What is actually happening?

lazyLoad triggers on every re-rerender.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 26 (16 by maintainers)

Commits related to this issue

Most upvoted comments

Could you give me more insights about the work you are doing right now? I’d like to provide as many help as I can to help you out of this issue. If you do feel like you can share that with me please do not hesitate to ping me.

@JeremyWuuuuu @SimonaliaChen Could you consider reopening this issue given it’s technically viable?

Done. I will revert https://github.com/element-plus/element-plus/pull/2298 and submit another pr ASAP

@SimonaliaChen You can use isEqualWith instead, in this case:

import { isEqualWith, isFunction } from 'lodash-es'

const o1 = { fn() {} }

const o2 = { fn() {} }

const equal = isEqualWith(o1, o2, (v1, v2) =>
  // if `customizer` returns `undefined`, comparisons are handled by the method instead
  isFunction(v1) && isFunction(v2) ? `${v1}` === `${v2}` : undefined,
)

console.log({ equal }) // { equal: true }

Same goes to the object literal. In your code

mport { defineComponent, ref } from 'vue'

export default defineComponent({
  setup() {
    const cascader = ref([])
    let id = 0

    return () => (
      <ElCascader
        vModel={cascader.value}
        // this creates a new object for each render calls which ended up allocate the object into different memory address.
        // so that vue takes it as **changed**
        props={{
          lazy: true,
          // 👇 triggers on every re-render, should be checked by content, e.g. using lodash's isEqual
          lazyLoad(node, resolve) {
            const { level } = node
            setTimeout(() => {
              const nodes = Array.from({ length: level + 1 }).map(() => ({
                value: ++id,
                label: `选项${id}`,
                leaf: level >= 2,
              }))
              // 通过调用resolve将子节点数据返回,通知组件数据加载完成
              resolve(nodes)
            }, 1000)
          },
        }}
      />
    )
  },
})

If you do this:

mport { defineComponent, ref } from 'vue'

export default defineComponent({
  setup() {
    const cascader = ref([])
    const props = {
          lazy: true,
          // 👇 triggers on every re-render, should be checked by content, e.g. using lodash's isEqual
          lazyLoad(node, resolve) {
            const { level } = node
            setTimeout(() => {
              const nodes = Array.from({ length: level + 1 }).map(() => ({
                value: ++id,
                label: `选项${id}`,
                leaf: level >= 2,
              }))
              // 通过调用resolve将子节点数据返回,通知组件数据加载完成
              resolve(nodes)
            }, 1000)
          },
        }
    let id = 0

    return () => (
      <ElCascader
        vModel={cascader.value}
        // this doesn't really have to be a reactive, simply use it as a plain object or you can use `Readonly`
        props={props}
      />
    )
  },
})