Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Infinite Table updates? #191

Description

@kikuomax

Overview of the problem

Buefy version: [0.1.2]
Vuejs version: [3.3.7]
OS/Browser: macOS/test-utils

Description

Table caused infinite updates of reactive states.

[Vue warn]: Maximum recursive updates exceeded in component <BTable>. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.

Steps to reproduce

  1. Merge changes in the original buefy repo (Merge changes in buefy/buefy from b6233b9c335d99cf688ee2a3b9c61fd26a92a182 to v0.9.28 #159)
  2. cd packages/buefy-next
  3. npx jest src/components/table/TableColumn.spec.js

Expected behavior

No infinite updates.

Actual behavior

Infinite updates.

Activity

  1. added this to the v0.1.5 milestone on Jul 22, 2024
  2. moved this from 🆕 New to 🔖 Ready in Buefy-next Vue3 Project Boardon Aug 10, 2024
  3. kikuomax commented on Aug 31, 2024

    @kikuomax
    CollaboratorAuthor

    This might be caused by values bound to <b-slot-component>, which are return values of methods; i.e., a brand new object is created every time the method is called.

    <b-slot-component
    :component="column"
    v-bind="column.tdAttrs(row, column)"
    scoped
    name="default"
    tag="td"
    :class="column.getRootClasses(row)"
    :style="column.getRootStyle(row)"
    :data-label="column.label"
    :props="{
    row, column, index, colindex,
    toggleDetails, isActiveDetailRow
    }"
    @click="$emit('cellclick',row,column,index,colindex)"
    />

    I found a case where the recursion error disappeared if I removed all of the v-bind, :class, and :style bindings.

  4. wesdevpro commented on Aug 31, 2024

    @wesdevpro

    This might be caused by values bound to <b-slot-component>, which are return values of methods; i.e., a brand new object is created every time the method is called.

    <b-slot-component
    :component="column"
    v-bind="column.tdAttrs(row, column)"
    scoped
    name="default"
    tag="td"
    :class="column.getRootClasses(row)"
    :style="column.getRootStyle(row)"
    :data-label="column.label"
    :props="{
    row, column, index, colindex,
    toggleDetails, isActiveDetailRow
    }"
    @click="$emit('cellclick',row,column,index,colindex)"
    />

    I found a case where the recursion error disappeared if I removed all of the v-bind, :class, and :style bindings.

    As a result did the CPU usage go down to?

  5. kikuomax commented on Aug 31, 2024

    @kikuomax
    CollaboratorAuthor

    As a result did the CPU usage go down to?

    @wesdevpro I was not able to measure the CPU usage because I just ran a tiny Vitest test case.

  6. self-assigned this
    on Aug 31, 2024
  7. wesdevpro commented on Aug 31, 2024

    @wesdevpro

    As a result did the CPU usage go down to?

    @wesdevpro I was not able to measure the CPU usage because I just ran a tiny Vitest test case.

    That makes sense

  8. modified the milestones: v0.2.0, v0.2.1 on Jan 14, 2025
  9. wesdevpro commented on Jan 14, 2025

    @wesdevpro

    @kikuomax do you want to tackle this issue in v0.2.0 or v0.2.1?

  10. kikuomax commented on Jan 14, 2025

    @kikuomax
    CollaboratorAuthor

    @wesdevpro I think that fixing this issue should be tough. Although it is a difficult decision as this bug is critical, let's put this issue in the v0.2.1 milestone to finish the TypeScript migration as soon as possible.

  11. moved this from 🔖 Ready to 🏗 In progress in Buefy-next Vue3 Project Boardon Feb 4, 2025
  12. kikuomax commented on Feb 6, 2025

    @kikuomax
    CollaboratorAuthor

    I found the problem was caused by passing an arrow function to the td-attrs prop in TableColumn.spec.ts.

    <BTableColumn :td-attrs="(td) => ({ id: \`td-\${td.id}\` })" v-slot="props">

    If I replaced the arrow function with a component instance method tdAttrs, the error disappeared.

            const DetailedTable = {
                template: `
                    <BTable :data="tableData" detail-key="id" detailed>
                        <BTableColumn :td-attrs="tdAttrs" v-slot="props">
                            <div>
                                {{ props.row.name }} - {{ props.isActiveDetailRow(props.row) ? 'Opened' : 'Closed' }}
                            </div>
                            <BButton @click="props.toggleDetails(props.row)">Toggle details</BButton>
                        </BTableColumn>
                    </BTable>`,
                components: { BTable, BTableColumn, BButton },
                methods: {
                    tdAttrs(td: { id: string }) {
                        return { id: `td-${td.id}` }
                    }
                }
            }

    I need some investigation and thinking to understand why this matters.

  13. kikuomax commented on Feb 7, 2025

    @kikuomax
    CollaboratorAuthor

    If we specify an inline arrow function to a prop, the prop is detected as changed every time the host component is evaluated. See this comment in the Vue.js core repo.

    I slipped a few lines for tracing update events and data changes. If I ran the test, I got the following output:

    Table visibleColumns changed
    Table beforeUpdate
    TableColumn tdAttrs changed
    TableColumn beforeUpdate
    Table beforeUpdate
    TableColumn tdAttrs changed
    TableColumn beforeUpdate
    Table beforeUpdate
    ...
    truncated
    ...
    TableColumn tdAttrs changed
    TableColumn beforeUpdate
    TableColumn updated
    Table updated
    
    [Vue warn]: Maximum recursive updates exceeded in component <BTable>. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.
    

    If I replaced tdAttrs in the test component with a component instance method, I got the following trace:

    Table visibleColumns changed
    Table beforeUpdate
    Table updated
    

    My hypothesis of how the infinite updates happen: Table is watching TableColumn specified to its default slot because it lists TableColumn in the visibleColumns computed value. Meanwhile, TableColumn is detected as changed every time it is evaluated, because an inline arrow function is specified to its tdAttrs prop (see this comment in the Vue.js core repo). Whenever Table enumerates items in the visibleColumns computed value, it will trigger an update of Table itself due to TableColumn's change, and then Table ends up with infinite updates.

  14. kikuomax commented on Feb 7, 2025

    @kikuomax
    CollaboratorAuthor

    I will fix the spec and add a warning message to the documentation to tell the consequence of specifying an inline arrow function to the tdAttrs prop.

  15. added a commit that references this issue on Mar 1, 2025
  16. moved this from 🏗 In progress to Pending Release🔦 in Buefy-next Vue3 Project Boardon Apr 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions