【问题标题】:How to not trigger svelte store subscription if the value was not changed如果值未更改,如何不触发 svelte store 订阅
【发布时间】:2023-01-23 11:54:45
【问题描述】:

我正在尝试弄清楚如何正确使用 Svelte 商店。 在我的代码中,我有一个商店,它的初始值要么来自 localStore if set,要么来自 const,我从未在没有用户操作的情况下对该商店调用 set 或 update。在其他组件中,该商店有一个订阅者,它在每次更改时都执行服务器请求(我希望请求仅在商店发生时发生变化), 但是我注意到在 app init 上请求被触发 (订阅回调被调用)

在这里查看文档 https://svelte.dev/tutorial/writable-stores

count.subscribe(value => {
    countValue = value;
});

我可以看到订阅回调在我单击任何按钮之前就已经运行了一次。 我如何才能只订阅商店更改(考虑到我传递给 writeable 的设置默认值不是“更改”)?

【问题讨论】:

    标签: svelte svelte-store


    【解决方案1】:

    您必须为自己构建一些实用程序。当您知道 Svelte 的商店合同时,这非常简单,它本身非常小。

    这样的事情会起作用:

    import { writable } from 'svelte/store'
    
    const events = store => ({
        ...store,
        subscribe(subscriber) {
            let initial = true
            
            const unsubscribe = store.subscribe($count => {
                if (!initial) {
                    subscriber($count)
                }
            })
            
            // the init call of the subscriber is made synchronously so, by
            // now, we know any further call is a change
            initial = false
            
            return unsubscribe
        }
    })
    
    export const count = writable(0)
    
    // countEvents will only call its subscribers for changes that happen
    // after the call to subscribe
    export const countEvents = events(count)
    

    然后您可以正常使用这个“事件存储”。例如(REPL):

    (注意 $countEventsundefined 直到底层的 count 存储实际改变,因为 countEvent 的订阅者在订阅时没有被调用。)

    <script>
        import { onMount } from 'svelte'
        import { count, countEvents } from './stores'
        
        const increment = () => count.update(x => x + 1)
        const decrement = () => count.update(x => x - 1)
        
        let logs = []
        
        onMount(() => {
            return count.subscribe((value) => {
                logs = [...logs, value]
            })
        })
        
        let changeLogs = []
        
        onMount(() => {
            return countEvents.subscribe((value) => {
                changeLogs = [...changeLogs, value]
            })
        })
    </script>
    
    <p>
        <button on:click={decrement}>-</button>
        <button on:click={increment}>+</button>
        {$count} / {$countEvents}
    </p>
    
    <table>
        <caption>Calls</caption>
        <thead>
            <tr>
                <th>subscribe</th>
                <th>subscribeChanges</th>
            </tr>
        </thead>
        {#each logs as log, i}
            <tr>
                <td>{log}</td>
                <td>{changeLogs[i] ?? ""}</td>
            </tr>
        {/each}
    </table>
    

    【讨论】:

    • 对于应该非常简单的东西(IMO),这是很多代码。希望他们会添加一种简单的方法来控制您是否要触发初始更新,这是一种常见的情况
    • 正如我的示例所示 (IMO),您自己使用几行代码就可以轻松完成。构建什么不会那么简单需要它,如果基础商店 API 试图满足每个用例任何人可能需要。我认为像这样的东西成为核心的可能性基本上为零。我认为它宁愿属于某种商店实用程序库(或您自己的集合)......所以,这是否回答了您的问题?
    【解决方案2】:

    我使用了@rixo 的示例,并制作了另一个版本作为工厂方法,它返回一个可写对象并提供一个订阅方法,让您可以选择跳过初始值,以便调用者可以决定。

    import { writable, type StartStopNotifier } from 'svelte/store';
    
    export function newStore<T>(value?: T, start?: StartStopNotifier<T>) {
      const store = writable(value, start);
    
      return {
        ...store,
        subscribe(subscriber: (arg: T) => void, skipInitial = false) {
          let initial = true;
          const unsubscribe = store.subscribe(($value) => {
            if (!skipInitial || !initial) {
              subscriber($value);
            }
          });
          initial = false;
          return unsubscribe;
        }
      };
    }
    

    创建商店实例:

    export const myStore = newStore('default value');
    

    订阅,注意第二个参数为 true 以跳过初始值:

    myStore.subscribe((value) => onValueChange(value), true);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-27
      • 2021-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多