【问题标题】:How to correctly work with non-primitive svelte stores?如何正确使用非原始苗条商店?
【发布时间】:2021-03-30 14:25:11
【问题描述】:

我不明白如何在组件中使用可写存储并且仍然让 DOM 对状态的变化做出反应。

我想更改存储状态对象的键并检索其最新值,然后在组件中使用这些值。

商店确实更新正确。唯一的问题是在存储状态更改时更新 DOM。

状态和商店类型

import { Writable, writable } from 'svelte/store';

export interface Words { [key: string]: boolean };

export interface SelectedWordsStore extends Writable<Words> {
  setWord: (key: string, state: boolean) => void;
  isWordSelected: (key: string) => boolean;
  reset: () => void;
}

商店


function createSelectedWordsStore(): SelectedWordsStore {
  const state: Words = {};
  const store: Writable<Words> = writable(state);

  return {
    ...store,
    setWord: (key: string, isSelected: boolean) => store.update((words) => {
        words[key] = isSelected;
        return words;
    }),

    isWordSelected: (key: string) => {
      return state[key] ?? false;
    },

    reset: () => store.set({}),
  }
}

export const selectedWordsStore = createSelectedWordsStore();

用法

它被用在一个苗条的组件和一个 TS 帮助文件中

<script lang="ts">
    import Word from './components/Word.svelte';
    import { words, getWordKey } from './helpers/words';
    import type { IndexProps } from './helpers/words';
    import { isBingo } from './helpers/bingo';
    import { selectedWordsStore } from './selected-words.store';

    const onWordClick = ({ currentTarget }: TypedMouseEvent<HTMLDivElement>): void => {
        const parent = currentTarget.parentElement;
        const columnId = parent.dataset.columnId;
        const wordId = currentTarget.dataset.id;
        const key = getWordKey({ columnId, wordId })

        selectedWordsStore.setWord(
            key,
            !selectedWordsStore.isWordSelected(key),
        )

        isBingo(...); // <--- store used here as well
    }

    function isWordSelected(options: IndexProps): boolean {
        return selectedWordsStore.isWordSelected(getWordKey(options));
    }
</script>

<div class="container" data-testid="container">
    {#each words as wordGroup, columnId}
        <div data-column-id={columnId}>
            {#each wordGroup as word, wordId}

                <Word
                    on:click={onWordClick}
                    name={word}
                    isSelected={isWordSelected({ columnId, wordId })} <!-- USAGE -->
                    wordId={wordId}
                />

            {/each}
        </div>
    {/each}
</div>

想要的结果

我希望在为 DOM 调用 onWordClick 后重新检查 isWordSelected 并将最新的布尔值从商店传递给 Word 组件

更新

我找到了一个可行的解决方案

isSelected={$selectedWordsStore && isWordSelected({ columnId, wordId })}

但是,不确定这是最好的方法。有什么建议吗?

更新 2

现在 store 上的 reset 回调会阻止 DOM 上的反应。 IE。按下重置后,DOM 或助手中不会反映任何状态更改。

【问题讨论】:

    标签: typescript svelte svelte-3 sapper svelte-store


    【解决方案1】:

    您的原始代码不起作用的原因是 isSelected={isWordSelected({ columnId, wordId })} 行将在组件渲染时执行,并且仅在那个时刻执行。表示渲染后的任何更改都不会显示。

    您提出的解决方案通过让商店也成为等式的一部分来解决这个问题,当商店发生变化时,整个store &amp;&amp; isSelected 部分将重新执行。

    在我看来,最好的方法是使用辅助方法放弃所有额外的复杂性,并直接从商店请求值:

      isSelected={$selectedWordsStore[getWordsKey({ columnId, wordId })]}
    

    此代码将执行与您的代码相同的操作,只是它直接从商店中读取值,并且因为它直接引用商店,所以它会相应地更新。

    【讨论】:

    • 您好,谢谢您的建议。我的目标是抽象商店访问,以便商店的客户都不知道实现细节。因此,即使您的解决方案有效,我也不会选择它。 :)
    • 在这种情况下,只有您的解决方案才能工作:)
    • 啊,注意到一个有趣的错误。如果我调用store.reset(),DOM 将停止响应存储更改。为什么会这样?
    • @RolandJegorov 如果你想抽象访问 store,那么你可以使用 smart/dumb (presenter/container) 组件架构,那么只有少数智能组件会知道 store 的实现细节。跨度>
    • @WillTaylor 当然,但这超出了这个问题的重点。还是谢谢你的建议。 :) 我现在最担心的是重置商店的错误。 github.com/Rolandisimo/bs-bingo/pull/1
    猜你喜欢
    • 2019-10-22
    • 2021-05-11
    • 2023-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-21
    • 2021-06-04
    • 2022-11-11
    相关资源
    最近更新 更多