如何正确使用非原始苗条商店?

时间:2020-12-20 16:47:20

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

我不明白如何在组件中使用可写存储并且仍然让 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();

用法

它被用在一个 svelte 组件和一个 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 上的重置回调会阻止 DOM 上的反应性。 IE。按下重置后,DOM 或帮助程序中不会反映任何状态更改。

1 个答案:

答案 0 :(得分:1)

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

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

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

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

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