Reputation: 13
I'm encountering an issue in my Vue application where the use of functional components within a table leads to entire re-renders of all cells when any cell's value changes, triggering a backend request and subsequently updating a list of cells based on the response. However, when I switch to using stateful template components for these cells, only the affected cell re-renders upon update.
Environment:
Problem Detail: My table component renders each cell as a separate component. The table includes column aggregators, and cell value changes initiate a backend request. The response dictates which cells to update. Using functional components for these cells results in the entire table re-rendering, but switching to standard stateful template components limits the re-render to only the changed cell.
// table.vue
<tr v-for="row in cells" :key="row[0].id">
<TableCellHeader />
<TableCell v-for="(cell, idx) in row.slice(1)"
:key="row[0].data.id+'_'+idx"
:cell="cell"
:col-index="idx+1"
:row-id="row[0].id"
:disable="isDisabled(row[0], cell, idx+1)"
@saved="save" />
</tr>
// tableCell.vue
<script>
import Stack from '@ej/components/stack';
import { updateValue } from '@ej/spo/api';
import dompurify from 'dompurify';
export default {
functional: true,
props: {
cell: {
type: Object,
required: true,
},
disable: {
type: Boolean,
default: false,
},
rowId: {
type: Number,
required: true,
},
},
render(h, { props, $style, listeners }) {
let element;
// some other class like validation before request
let setClass = (className) => {
if (!element) return;
let classNames = ['error', 'saved', 'saving'];
classNames.forEach((name) => {
element.classList.remove(name);
});
if (!className) return;
element.classList.add(className);
};
let save = (updatedValue, cell, rowId) => {
// post to server
updateValue(updatedValue)
.then((result) => {
setClass('saved');
setTimeout(() => {
setClass('');
}, 3000);
listeners.saved?.(result);
})
.catch(e => {
setClass('error');
});
};
let onFocusOut = (event) => {
element = event.target.closest('td');
let updatedValue = event.target.innerText.trim();
setClass('saving');
event.target.innerHTML = dompurify.sanitize(updatedValue);
save(updatedValue, props.cell, props.rowId);
};
return h('td', {
class: [$style.cell, 'spo-table-cell'],
attrs: {
'data-disabled': props.disable,
},
}, [
h(Stack, {
props: {
verticalAlign: 'center',
fill: true,
},
}, [
h('div', {
attrs: {
contenteditable: !props.disable,
},
class: $style.content,
on: {
blur: onFocusOut,
},
domProps: {
innerHTML: props.cell.value || '',
},
}),
]),
]);
},
};
</script>
<style lang="less" module>
// some styles
</style>
The first is render and second is re-render after cell update
Performance of update. Function createFunctionalComponent has been called 802 times
What could be causing this discrepancy in re-render behavior between functional components and stateful components in Vue? How can I ensure that only the updated cells re-render in the table when using functional components, similar to the behavior observed with stateful template components?
Upvotes: 1
Views: 63