Svelte派生存储返回错误值问题排查
Svelte待办表单验证问题排查与解决
问题场景
实现了基于Svelte store的待办事项表单,将表单逻辑抽象到store中,使用派生store error 处理提交验证。但点击「Add Todo」按钮时,首次会添加空待办项,之后才显示对应的错误提示。
代码示例
App.svelte
<script> import { todo, todos, count, addTodo, error } from "./stores"; </script> <div> <input type="text" bind:value={$todo.text} /> {#if $error} <span class="error">{$error}</span> {/if} <button on:click={addTodo}>Add Todo</button> <!-- show todo items --> </div>
stores.js
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 ""; } });
问题原因
- 派生store依赖缺失:
error派生store仅声明依赖todo,但内部逻辑实际依赖formState的状态。当formState从idle变为submitted时,不会触发error的重新计算,此时get(error)拿到的还是旧值(undefined)。 - 同步获取store值的时序问题:在
addTodo中,设置formState为submitted后立刻调用get(error),此时error还未完成重新计算,导致判断!_error为true,执行了添加空todo的逻辑。 - 验证触发滞后:只有当后续
todo被清空(todo.update)时,error才会因为todo的变化重新计算,此时才返回错误信息,但空todo已经被添加到列表中。
解决方案
1. 修正派生store的依赖
让error同时依赖todo和formState,确保两者任一变化都会触发验证逻辑:
export const error = derived([todo, formState], ([$todo, $formState]) => { if ($formState.status === "submitted") { if (!$todo.text.trim()) { return "请输入任务内容"; } else if ($todo.text.length < 3) { return "任务内容至少需要3个字符"; } return ""; } return undefined; });
2. 调整addTodo的验证逻辑
避免同步get(error),改为直接在函数内完成验证,逻辑更清晰:
export const addTodo = () => { formState.set({ status: "submitted" }); const currentTodo = get(todo); let validationError = ""; if (!currentTodo.text.trim()) { validationError = "请输入任务内容"; } else if (currentTodo.text.length < 3) { validationError = "任务内容至少需要3个字符"; } if (!validationError) { todos.update(todos => [...todos, currentTodo]); todo.set({ text: "", completed: false }); formState.set({ status: "idle" }); } };
内容的提问来源于stack exchange,提问作者Austin Walela
相关产品推荐
相关产品推荐

