Home > Software design >  Derived store returns incorrect value
Derived store returns incorrect value

Time:07-29

I have a simple text input that looks like this

// App.svelte

<script>
  import { todo, todos, count, addTodo, error } from "./stores";
</script>

<div>
    <input type="text" bind:value={$todo.text} />
        {#if $error}
            <span >{$error}</span>
        {/if}
    <button on:click={addTodo}>Add Todo</button>
<!-- show todo items -->
</div>

Everything is abstracted out neatly into a store which looks like this:

import { writable, derived, get } from "svelte/store";

export const todo = writable({ text: "", completed: false });
export const todos = writable([{ text: "Learn Svelte", completed: false }]);

const formState = writable({ status: "idle" });

export const addTodo = () => {
  formState.set({ status: "submitted" });
  let _error = get(error);
  if (!_error) {
    let _todo = get(todo);
    todos.update((todos) => {
      todos.push(_todo);
      todo.update(() => ({ text: "", completed: false }));
      return todos;
    });
    formState.set({ status: "idle" });
  }
};

...

export const error = derived(todo, todo => {
  if (get(formState).status === "submitted") {
    if (!todo.text.trim()) {
      return "Please enter a task";
    } else if (todo.text.length < 3) {
      return "Todo must be at least 3 characters long";
    }
    return "";
  }
});

I'm sure there's better ways to do form validation but the purpose of this example is to play around with Svelte stores. I have created an error derived store that does some validation once the form is submitted by subscribing to the todo value. When I click the 'Add Todo' button initially, I get the correct error, but an empty todo is rendered (and apparently the error is undefined), then the form validation kicks in subsequently. Why is this?

Codesandbox - https://codesandbox.io/s/stupefied-chaplygin-vzvg7e

CodePudding user response:

Your derived store is implemented incorrectly: It does not list all stores it depends on.

If you ever use get you are losing reactivity, so that can be a sign something is wrong. In this case the formState needs to be included, otherwise the store will not update if that state changes:

export const error = derived([todo, formState], ([$todo, $formState]) => {
  if ($formState.status === "submitted") {
    if (!$todo.text.trim()) {
      return "Please enter a task";
    } else if ($todo.text.length < 3) {
      return "Todo must be at least 3 characters long";
    }
    return "";
  }
});

(Would recommend the $ prefix convention to not shadow variables and indicate that something is a store value, rather than the store itself.)

  • Related