Refactor watch sources for reliability

This commit changes `WatchSource` signatures into `Readonly<Ref>`s.

It provides two important benefits:

1. Eliminates the possibility of `undefined` states, that's result of
   using `WatchSource`s. This previously required additional null checks.
   By using `Readonly<Ref>`, the state handling becomes simpler and less
   susceptible to null errors.
2. Optimizes performance by using references:
   - Avoids the reactive layer of `computed` references when not needed.
   - The `watch` syntax, such as `watch(() => ref.value)`, can introduce
     side effects. For example, it does not account for `triggerRef` in
     scenarios where the value remains unchanged, preventing the watcher
     from running (vuejs/core#9579).
This commit is contained in:
undergroundwires
2023-11-11 13:55:21 +01:00
parent 58cd551a30
commit 7ab16ecccb
25 changed files with 190 additions and 217 deletions

View File

@@ -1,5 +1,5 @@
<template>
<div class="wrapper" v-if="currentNode">
<div class="wrapper">
<div
class="expansible-node"
@click="toggleCheck"
@@ -46,15 +46,14 @@
</template>
<script lang="ts">
import {
defineComponent, computed, PropType,
} from 'vue';
import { defineComponent, computed, toRef } from 'vue';
import { TreeRoot } from '../TreeRoot/TreeRoot';
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
import { NodeRenderingStrategy } from '../Rendering/Scheduling/NodeRenderingStrategy';
import { useNodeState } from './UseNodeState';
import { TreeNode } from './TreeNode';
import LeafTreeNode from './LeafTreeNode.vue';
import type { PropType } from 'vue';
export default defineComponent({
name: 'HierarchicalTreeNode', // Needed due to recursion
@@ -76,33 +75,32 @@ export default defineComponent({
},
},
setup(props) {
const { nodes } = useCurrentTreeNodes(() => props.treeRoot);
const currentNode = computed<TreeNode | undefined>(
() => nodes.value?.getNodeById(props.nodeId),
const { nodes } = useCurrentTreeNodes(toRef(props, 'treeRoot'));
const currentNode = computed<TreeNode>(
() => nodes.value.getNodeById(props.nodeId),
);
const { state } = useNodeState(() => currentNode.value);
const expanded = computed<boolean>(() => state.value?.isExpanded ?? false);
const { state } = useNodeState(currentNode);
const expanded = computed<boolean>(() => state.value.isExpanded);
const renderedNodeIds = computed<readonly string[]>(
() => currentNode.value
?.hierarchy
.hierarchy
.children
.filter((child) => props.renderingStrategy.shouldRender(child))
.map((child) => child.id)
?? [],
.map((child) => child.id),
);
function toggleExpand() {
currentNode.value?.state.toggleExpand();
currentNode.value.state.toggleExpand();
}
function toggleCheck() {
currentNode.value?.state.toggleCheck();
currentNode.value.state.toggleCheck();
}
const hasChildren = computed<boolean>(
() => currentNode.value?.hierarchy.isBranchNode,
() => currentNode.value.hierarchy.isBranchNode,
);
return {