I am curently using vue3-tree for my project and im trying to configure the nodes, im using pinia to store props.filter.data up to the parent, but i keep getting this error:
Uncaught (in promise) Maximum recursive updates exceeded. 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.
My component currently looks like this:
<template>
<div data-cy="filter-tree-list">
<tree
data-cy="filter-tree-list-component"
data-test="filter-tree-list"
class="flex flex-row w-full justify-start items-stretch rounded-md px-3 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300"
v-if="props.filter.data"
rowHoverBackground="#b2ffff40"
:nodes="props.filter.data"
:use-checkbox="true"
@update:nodes="onUpdate"
@nodeClick="onNodeClick"
>
</tree>
</div>
</template>
<script setup lang="ts">
import { inject, defineEmits, defineProps, ref, watch } from "vue";
import { Filter } from "../../types/common/filter";
interface Node {
id: string;
label: string;
nodes: Array<Node>;
checked?: boolean;
}
const piniaRef: unknown = inject("piniaStore");
const props = defineProps({
filter: {
type: Object as () => Filter,
required: true,
},
isReadOnly: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["filterChange"]);
const changed = ref(false);
const checkedIds = ref<string[]>([]);
const filterValueHasChanged = () => {
if (props.filter.value === "") {
clearCheckedIds(props.filter.data as unknown as Node[]);
}
};
watch(() => props.filter.value, filterValueHasChanged);
const onUpdate = (nodes: Node[]) => {
if (piniaRef && props.filter.value === undefined) return;
checkedIds.value = [];
getCheckedIds(nodes);
//vue 3 doesnt recommend changing v-model values as props, disable warning until fixed
// eslint-disable-next-line
props.filter.value =
checkedIds.value.length > 0 ? checkedIds.value.join(",") : "";
filterHasChanged();
if (piniaRef && props.filter.immediate) {
emitFilterChange();
}
};
const onNodeClick = (node: Node) => {
const checked = node.checked;
if (Array.isArray(node.nodes)) {
node.nodes.forEach((childNode) => {
childNode.checked = checked;
});
}
};
const getCheckedIds = (nodes: Node[]) => {
if (Array.isArray(nodes)) {
nodes.forEach((node) => {
if (Array.isArray(node.nodes)) {
getCheckedIds(node.nodes);
} else if (node.checked) {
checkedIds.value.push(node.id);
}
});
}
};
const clearCheckedIds = (nodes: Node[]) => {
if (Array.isArray(nodes)) {
nodes.forEach((node) => {
node.checked = false;
if (Array.isArray(node.nodes)) {
clearCheckedIds(node.nodes);
}
});
}
};
const filterHasChanged = () => {
changed.value = true;
};
const emitFilterChange = () => {
if (changed.value) {
emit("filterChange", props.filter);
changed.value = false;
}
};
</script>
This error keeps breaking in production causing an infinite rerender, but works fine in development. I have tried putting the props in a ref and in a computed method, but to no avail.
Any helps is greatly appreciated.
I have decided to use PrimeVue treeSelect as the vue3-tree library is fundamentally broken and not compatible with the latest version of vue 3. The component allows select and unselect props which allow me to select and add keys to an empty array:
<template>
<div class="card flex justify-content-center">
<TreeSelect
v-model="selectedValue"
:options="nodes"
selectionMode="checkbox"
placeholder="Select Item"
class="md:w-20rem w-full"
@nodeSelect="onNodeSelect"
@nodeUnselect="onNodeUnselect"
/>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { NodeService } from './service/NodeService';
const nodes = ref(null);
const selectedValue = ref(null);
const selectedIds = ref([])
onMounted(() => {
NodeService.getTreeNodes().then((data) => (nodes.value = data));
});
const onNodeSelect = (node) => {
console.log('selected', node.key)
selectedIds.value.push(node.key)
console.log(selectedIds.value)
}
const onNodeUnselect = (node) => {
let unselectedId = node.key
console.log('unselectedId: ' , unselectedId)
selectedIds.value = selectedIds.value.filter(key => key !== unselectedId);
console.log(selectedIds.value)
}
</script>