Svelte доступ к переменной хранилища в теге скрипта дочернего компонента

У меня есть приложение Svelte & Sapper, в котором я использую записываемое хранилище Svelte для настройки переменной с начальным значением пустой строки:

import { writable } from 'svelte/store';
export let dbLeaveYear = writable('');

В моем Index.svelte файле я импортирую это, а затем вычисляю значение этой переменной и устанавливаю его (я делаю это в функции onMount из `` Index.svelte, если это актуально):

<script>
  import {dbLeaveYear} from "../stores/store.js"

  function getCurrentLeaveYear() {
    const today = new Date();
    const currYear = today.getFullYear();
    const twoDigitYear = currYear.toString().slice(-2);
    const cutoffDate = `${twoDigitYear}-04-01`
    const result = compareAsc(today, new Date(cutoffDate));
    if (result === -1) {
      $dbLeaveYear = `${parseInt(twoDigitYear, 10) - 1}${twoDigitYear}`;
    } else {
      $dbLeaveYear = `${twoDigitYear}${parseInt(twoDigitYear, 10) + 1}`;
    }
  }

  onMount(() => {
    getCurrentLeaveYear();
  });
</script>

У меня есть дочерний компонент, отображаемый в Index.svelte

<Calendar />

Внутри дочернего компонента календаря я импортирую переменную и пытаюсь получить к ней доступ, чтобы выполнить преобразование, но я получаю сообщения об ошибке, так как она все еще пуста - похоже, она не принимает назначение от Index.svelte:

<script>
  import {dbLeaveYear} from "../stores/store.js"
  const calStart = $dbLeaveYear.slice(0, 2)
</script>

Однако, если я использую значение в элементе HTML в том же дочернем компоненте Calendar с <p>{$dbLeaveYear}</p>, оно заполняется значением из вычисления в Index.svelte.

Как мне получить доступ к переменной store внутри тега <script> дочернего компонента? Это вообще возможно? Я пробовал назначать onMount, пытался назначать функцию - похоже, ничего не работает, и всегда говорится, что $dbLeaveYear - это пустая строка.

Мне нужно, чтобы значение было динамическим, поскольку значение года отпуска может меняться.


person MarkB    schedule 12.04.2020    source источник


Ответы (2)


Прежде чем углубиться в вашу проблему, позвольте мне сказать, что вам не следует напрямую изменять переменную хранилища, а использовать предоставленный метод set или update. Это позволяет избежать трудных для отладки ошибок:

  if (result === -1) {
      dbLeaveYear.set(() => `${parseInt(twoDigitYear, 10) - 1}${twoDigitYear}`);
    } else {
      dbLeaveYear.set(`${twoDigitYear}${parseInt(twoDigitYear, 10) + 1}`);
    }

После этого проблема, похоже, в том, что ваша автоматическая подписка на магазин не идеален для вашего случая использования. Для этого вам нужно использовать свойство subscribe:

<script>
  import { dbLeaveYear } from "../stores/store.js"
  import { onDestroy, onMount } from "svelte"

  let yearValue;
  // Needed to avoid memory leaks
  let unsubscribe

  onMount(() => {
    unsubscribe = dbLeaveYear.subscribe(value => yearValue = value.slice(0, 2));
  })

  onDestroy(unsubscribe);
</script>

Другая вещь, которая может вызвать вашу проблему, - это состояние гонки. Таким образом, обновление родительского компонента не завершается при рендеринге дочернего компонента. Затем вам нужно будет добавить проверку работоспособности в дочерний компонент рендеринга.

person Gh05d    schedule 15.04.2020
comment
Спасибо за сообщение. Я немного смущен тем, что вы говорите, что проблема заключается в том, что я использую синтаксис $, поскольку это совершенно допустимый синтаксис автоматической подписки, как описано на сайте svelte и в руководстве - svelte.dev/tutorial/auto-subscriptions - person MarkB; 16.04.2020
comment
Думаю ты прав. Похоже, проблема в том, что вы манипулируете переменной при монтировании компонента, который потенциально не определен в этот момент. Также это зло, поскольку существуют геттеры и сеттеры, которые могут вызывать ошибки. Вы пробовали мой синтаксис? - person Gh05d; 16.04.2020
comment
Я попробую на этих выходных и дам вам знать. Спасибо за продолжение. - person MarkB; 17.04.2020
comment
Поэтому я думаю, что это Sapper / SSR, а не связанный с Svelte / Store, поскольку я переписал код, сделав его локальной переменной в Index.svelte, и передал его Calendar.svelte в качестве опоры, и он все еще остается пустым. Читая документацию на Sapper, я думаю, это потому, что я делаю все вычисления для переменных в функции onMount, которая выполняется только на клиенте, поэтому в момент оценки кода в моем теге ‹script›, vaiable все еще изначально объявлен банковская строка. Мне как-то нужно сделать серверную часть вычислений, поэтому я исследую, как это сделать. - person MarkB; 19.04.2020
comment
Ах хорошо. К сожалению, у меня нет опыта работы с Sapper, поэтому я не буду здесь никакой помощи. Удачи. - person Gh05d; 19.04.2020

Ответ здесь - комбинация предварительной загрузки Sapper и возможности экспортировать функцию из магазина.

в store.js экспортируйте доступное для записи хранилище для нужной переменной, а также функцию, которая будет определять значение и устанавливать доступное для записи хранилище:

export let dbLeaveYear = writable('');

export function getCurrentLeaveYear() {
  const today = new Date();
  const currYear = today.getFullYear();
  const twoDigitYear = currYear.toString().slice(-2);
  const cutoffDate = `${twoDigitYear}-04-01`
  const result = compareAsc(today, new Date(cutoffDate));
  if (result === -1) {
    dbLeaveYear.set(`${parseInt(twoDigitYear, 10) - 1}${twoDigitYear}`);
  } else {
    dbLeaveYear.set(`${twoDigitYear}${parseInt(twoDigitYear, 10) + 1}`);
  }
}

В .svelte файле верхнего уровня используйте функцию Sapper preload() внутри тега сценария "module", чтобы вызвать функцию, которая будет определять значение и устанавливать доступное для записи хранилище:

<script context="module">
  import {getCurrentLeaveYear} from '../stores/store'
  export async function preload() {
        getCurrentLeaveYear();
  }
</script>

Затем в файле компонента .svelte вы можете импортировать переменную хранилища, и поскольку она была предварительно загружена, она будет доступна в теге <script>:

<script>
  import {dbLeaveYear} from '../stores/store'

  $: startDate = `20${$dbLeaveYear.slice(0, 2)}`
  $: endDate = `20${$dbLeaveYear.slice(-2)}`
</script>
person MarkB    schedule 03.05.2020