Svelte 5:将状态和派生值从子级传递到父级(没有存储的符文模式)

问题描述 投票:0回答:1

我有一个带有多个可拖动项目组件的父组件,这些组件将在最终应用程序中动态创建。在每个项目内,我计算当前位置以及派生的偏移值。每次拖动任何 Item 时,我都希望其位置/偏移量与父组件反应式共享。

在带有商店的 Svelte 4 中,这相当容易。然而,我很难在 Svelte 5 中使用符文实现这一点。 Context-API 不知何故不是一个选项,因为我想在它们所属的 Item 组件内定义 pos/offset...不应该吗?

当前代码有两个问题:

  • (1) 当通过绑定 props 将位置(状态)发送到父级时,这对于偏移量(派生)不起作用。为什么?
  • (2) 一旦安装了第二个项目组件,第一个项目和父项目之间的连接就会丢失。为什么?

更新2024-05-14

这是我的代码的简化版本(REPL)

App.svelte

<svelte:options runes="{true}" />

<script>
    import Item from './Item.svelte';

    let CURRENT = $state({pos: {x:99, y:99}, offset: 99});

</script>

<h1>Last dragged item: ({CURRENT.pos.x} / {CURRENT.pos.y}) Offset: {CURRENT.offset}</h1>

<Item bind:CURRENT={CURRENT} itemName='ONE' />
<Item bind:CURRENT={CURRENT} itemName='TWO' />

Item.svelte

<svelte:options runes="{true}" />

<script>
    import interact from 'interactjs';

    let { CURRENT = $bindable(), itemName } = $props();
    let item;
    
    let pos = $state({x:0, y:0});
    let offset = $derived(Math.floor(Math.sqrt( Math.pow(pos.x,2) + Math.pow(pos.y,2) )));

    CURRENT.pos = pos;
    CURRENT.offset = offset;

    const handleDraggable = (node) => {
        interact(node).draggable({              
            onmove: (ev) => {
                let el = ev.target;
        
                let x = (parseFloat(el.getAttribute('data-x')) || 0) + ev.dx;
                let y = (parseFloat(el.getAttribute('data-y')) || 0) + ev.dy;
                
                el.style.webkitTransform =  el.style.transform = `translate(${x}px,${y}px)`;
                el.setAttribute('data-x', x);
                el.setAttribute('data-y', y);
                pos.x = x;
                pos.y = y;
            },
        });
    }
</script>


<div id="draggable" bind:this={item} use:handleDraggable> 
    <p>{itemName} ({pos.x} / {pos.y}) Offset: {offset}</p>
</div>
components svelte svelte-5
1个回答
0
投票

正如 @PeppeL-G 在我的问题的评论中所暗示的,可以使用 回调函数将变量从子级传递到父级。

这是更改后的代码和REPL

App.svelte

<svelte:options runes="{true}" />

<script>
    import Item from './Item.svelte';
    
    let CURRENT = $state({pos: {x:99, y:99}, offset: 99});
    let onDragged = (pos, offset) => { 
        CURRENT.pos = pos, 
        CURRENT.offset = offset
    }
</script>

<h1>Last dragged item: ({CURRENT.pos.x} / {CURRENT.pos.y}) Offset: {CURRENT.offset}</h1>

<Item itemName='ONE' {onDragged} } />
<Item itemName='TWO' {onDragged} } />

Item.svelte

<svelte:options runes="{true}" />

<script>
        import interact from 'interactjs';

    //let { CURRENT = $bindable(), itemName } = $props();
    let { itemName, onDragged } = $props();
    let item;
    
    let pos = $state({x:0, y:0});
    let offset = $derived(Math.floor(Math.sqrt( Math.pow(pos.x,2) + Math.pow(pos.y,2) )));

    const handleDraggable = (node) => {
            interact(node).draggable({              
                onmove: (ev) => {
                    let el = ev.target;
        
                    let x = (parseFloat(el.getAttribute('data-x')) || 0) + ev.dx;
                    let y = (parseFloat(el.getAttribute('data-y')) || 0) + ev.dy;
                
                    el.style.webkitTransform =  el.style.transform = `translate(${x}px,${y}px)`;
                    el.setAttribute('data-x', x);
                    el.setAttribute('data-y', y);
                    pos.x = x;
                    pos.y = y;
                                onDragged(pos, offset);
                },
            });
        }
</script>


<div id="draggable" bind:this={item} use:handleDraggable> 
    <p>{itemName} ({pos.x} / {pos.y}) Offset: {offset}</p>
</div>


<style>
    #draggable {
        background-color: green;
          width: 220px;
    }
</style>
© www.soinside.com 2019 - 2024. All rights reserved.