Svelte中如何等待fetch完成后再赋值以避免运行时错误?
问题:Svelte动态计算总价时启动出现undefined错误
问题场景
在Svelte项目中,从后端获取物品列表后尝试通过响应式语句动态计算选中物品的总价,但启动时出现undefined错误。相关代码及数据如下:
父组件原代码:
<script> // ../ import stuff let items; const getItems = async() => { const resp = await fetch('localhost:8080/items'); const result = await resp.json(); items = result; } onMount(async() => {getItems()}) $: totalPrice = items.filter(i => i.amount !== 0).reduce((sum,obj) => {return (sum+obj)},0); <!-- 运行时此处报错 --> </script> function purchase() { //do stuff }
Counter.svelte原代码:
<script> export let amount = 0; function increment() { amount += 1; } function decrement() { if (amount > 0) { amount -= 1; } } </script> <p>{totalPrice}</p> <button on:click={decrement}>-</button>{amount}<button on:click={increment}>+</button>
后端返回数据示例:
let items=[{ id: 1, name: 'potato', price: 5 }, { id: 2, name: 'garlic', price: 3 }, { id: 3, name: 'rice', price: 10 }];
添加数量后的物品数据示例:
[ { id: 3 , name: "rice" , price: 10 , amount: 3 }]
问题原因
items初始未赋值,默认是undefined,组件挂载后立即执行响应式语句时,undefined.filter()直接触发报错。- 后端返回的物品数据缺少
amount字段,后续过滤判断会出现逻辑问题。 reduce逻辑错误,直接累加对象而非计算「价格×数量」的总和。- Counter组件未将数量变化同步回父组件的
items数组,无法触发总价的动态更新。
解决方案
1. 初始化items为空数组,避免undefined
给items设置初始空数组,确保组件启动时filter方法能正常执行:
let items = [];
2. 补全fetch地址并给物品添加初始amount字段
后端返回的物品没有amount字段,请求完成后给每个物品添加初始数量0;同时补全fetch的http://前缀(否则会被当作相对路径):
const getItems = async() => { const resp = await fetch('http://localhost:8080/items'); const result = await resp.json(); items = result.map(item => ({ ...item, amount: 0 })); }
3. 修正总价计算的reduce逻辑
总价应为「价格×数量」的累加,修正响应式计算语句:
$: totalPrice = items .filter(i => i.amount !== 0) .reduce((sum, obj) => sum + obj.price * obj.amount, 0);
4. 让Counter组件同步数量变化到父组件
Counter组件通过自定义事件把数量变化传递给父组件,父组件更新items数组触发响应式更新:
修正后的Counter.svelte:
<script> import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); export let amount = 0; function increment() { amount += 1; dispatch('update', amount); } function decrement() { if (amount > 0) { amount -= 1; dispatch('update', amount); } } </script> <button on:click={decrement}>-</button>{amount}<button on:click={increment}>+</button>
父组件中渲染Counter并处理更新事件:
{#each items as item} <div> <span>{item.name} - ¥{item.price}</span> <Counter {amount}={item.amount} on:update={(e) => { items = items.map(i => i.id === item.id ? { ...i, amount: e.detail } : i ); }} /> </div> {/each} <p>总价:¥{totalPrice}</p>
完整修正后的父组件代码
<script> import { onMount } from 'svelte'; import Counter from './Counter.svelte'; // 初始化空数组,避免undefined报错 let items = []; const getItems = async() => { const resp = await fetch('http://localhost:8080/items'); const result = await resp.json(); // 给每个物品添加初始amount字段 items = result.map(item => ({ ...item, amount: 0 })); } onMount(async() => { await getItems(); }) // 响应式计算总价 $: totalPrice = items .filter(i => i.amount !== 0) .reduce((sum, obj) => sum + obj.price * obj.amount, 0); function purchase() { // 执行结算逻辑 } </script> {#each items as item} <div style="margin: 10px 0;"> <span>{item.name} 单价:¥{item.price}</span> <Counter {amount}={item.amount} on:update={(e) => { items = items.map(i => i.id === item.id ? { ...i, amount: e.detail } : i ); }} /> </div> {/each} <div style="margin-top: 20px; font-weight: bold;"> 当前总价:¥{totalPrice} </div> <button on:click={purchase}>结算</button>
内容的提问来源于stack exchange,提问作者askwer
相关产品推荐
相关产品推荐

