I have a small Svelte app and love the framework, but I am running into a issue with local variables. There is a local
writable
variable declared in a child component and is only used in this component. It is updated when a on:click
event is fired and updates as it should. However, upon another on:click
(different function) event it resets this local variables value to the default value (as it should) and uses dispatch
to update the passed props in the parent.
Now the issue is that once the updated props are passed back to the child, the local writable
variable reverts to the previous value. What am I not seeing here? Thanks!
Parent:
<script lang="ts">
// Node Navigator Component
// This component contains all the sub components for the navigator
import type { IHistoryNode, ILinkedListNode } from "../../../types/linkedListNode";
import type { ILinkedList } from "../../../types/linkedList";
import type { IMockData } from "../../../types/mockData";
import { LinkedListNode } from "../../../utils/linkedListNode";
import NodeList from '../../molecules/nodeList/index.svelte';
// props
export let nodes: IMockData[];
// local vars
let currentNodes: IMockData[] = nodes;
// set the current node if id is passed and its connections
// else reset to original state
function setNodes(id?: string) {
if (id) {
currentNode = nodes.find(node => node.id === id);
currentNodes = [...nodes.filter(node => currentNode.connections.includes(node.id))];
} else {
currentNode = undefined;
currentNodes = nodes;
}
}
** this is the function that is called from the child to update the props **
// add the selected node is to the history list, set the state for the current node
function handleCurrentId(event: any) {
const {id, name} = event.detail;
const newNode: ILinkedListNode<IHistoryNode> = new LinkedListNode({id, name});
historyList.addNode(newNode);
historyList = historyList;
setNodes(id);
}
</script>
<section class={'nodeNavigatorWrapper'}>
** this is the child componet in question **
<NodeList
bind:nodes={currentNodes}
on:newId={handleCurrentId}
/>
</section>
Child:
<script lang="ts">
// Node List Component
// This component will render a list of the node passed from props
import { createEventDispatcher } from "svelte";
import { shareAlt, plus, minus } from 'svelte-awesome/icons';
import type { IMockData } from "../../../types/mockData";
import type { IHistoryNode } from '../../../types/linkedListNode';
import { Writable, writable } from 'svelte/store';
// props
export let nodes: IMockData[];
// local vars
const dispatch = createEventDispatcher();
let exapndedNodeIdx: Writable<number> = writable(-1);
// reset open row and disatch action to add selected to to navigator
function onOpenConnection(value: IHistoryNode) {
exapndedNodeIdx.set(-1);
dispatch('newId', { id: value.id, name: value.name })
};
// if the row that is click is open, close it. Else open it
function handleExpand(idx: number) {
exapndedNodeIdx.update((currIdx: number) => {
if (currIdx === idx) currIdx = -1;
else { currIdx = idx; }
return currIdx;
})
}
</script>
{#if nodes.length}
{console.log($exapndedNodeIdx)}
<section>
<table class="nodeTableWrapper">
{#each nodes as node, idx}
<tr
class="nodeTableRow"
on:click={() => handleExpand(idx)}
title={$exapndedNodeIdx === idx ? `Close ${node.name}` : `Expand ${node.name}`}
>
<th>
{#if $exapndedNodeIdx === idx}
<Icon data={minus} style={'margin-right: 0.5em'} />
{:else}
<Icon data={plus} style={'margin-right: 0.5em'} />
{/if}
{node.name}
</th>
{#if $exapndedNodeIdx === idx}
<td transition:slide="{{delay: 100, duration: 350, easing: quintOut }}">
<p>{node.summary}</p>
<span>Number of connected nodes: {node.connections.length}</span>
<button
type={'button'}
on:click={() => onOpenConnection({id: node.id, name: node.name})}
class="openNodeBtn"
aria-label={`Open ${node.name}`}
title={`Open ${node.name}`}
>
<Icon data={shareAlt} scale={1.2}/>
</button>
</td>
{/if}
</tr>
{/each}
</table>
</section>
{/if}
The store state should not be affected, but the component UI state might be inconsistent. Since you are switching out the entire node list, you probably need to use a keyed #each
:
{#each nodes as node, idx (node.id)}