[英]How do I force a rerender in Svelte when my props changes?
I have a totaling component that needs to total the same index of multiple arrays in a multidimensional array, similar to totaling columns in a spread sheet.我有一个总计组件需要在多维数组中总计多个 arrays 的相同索引,类似于电子表格中的总计列。 I have a different component that allows me to change the values in the arrays, and also totals the rows in the spreadsheet.我有一个不同的组件,它允许我更改 arrays 中的值,并汇总电子表格中的行。 I'm having troubles with the totaling component re-rendering and/or updating the columns total after a change to the array.我在对数组进行更改后重新渲染和/或更新列总数时遇到问题。 I thought I might need to do a reactive declaration in my totaling component, but that doesn't seem to be working.我想我可能需要在我的总计组件中做一个反应性声明,但这似乎不起作用。
Here's a link to the REPL.这是REPL 的链接。
And here's the code:这是代码:
Totals.svelte
<script>
export let jobs
//this creates a new array which is two indexes long for each of the columns
//then fills each array with the sum of column
//HOW DO I GET THIS TO UPDATE EACH TIME A JOB HOURS ENTRY IS CHANGED?
$: totals = new Array(2).fill(null).map((v, i) => {
let total = 0;
jobs.jobHours.forEach((v) => {
total += Number(v[i]);
});
return total;
});
//this sums the total hours row to give the grand total
//HOW DO I GET THIS TO UPDATE EACH TIME THE TOTALS VARIABLE IS CHANGED?
$: grandTotal = totals.reduce((acc, v) => (acc += v));
</script>
<style>
container {
display: grid;
grid-template-columns: 2fr 1fr 1fr 1fr;
}
div {
width: 100%;
height: 100%;
width: 150px;
}
</style>
<container>
<div>Total Hours</div>
{#each totals as total}
<div>
{total}
</div>
{/each}
<div>{grandTotal}</div>
</container>
App.svelte
<script>
import JobEntries from './JobEntries.svelte';
import Totals from './Totals.svelte';
const jobs = {
jobNames: ['Job1', 'Job2', 'Job3'],
jobHours: Array.from(Array(3), () => Array.from(Array(2), ()=>1))
};
</script>
<JobEntries {jobs}/>
<Totals {jobs} />
JobEntries.svelte
<script>
export let jobs
let cell = Array.from(Array(3), () => Array.from(Array(3)));
</script>
<style>
container {
display: grid;
grid-template-columns: 2fr 1fr 1fr 1fr;
}
input, div {
width: 100%;
height: 100%;
min-width: 150px;
}
</style>
<container>
<div>
Name
</div>
<div>
Time 1
</div>
<div>
Time 2
</div>
<div>
Total
</div>
{#each jobs.jobNames as name, i}
<input
type="text"
bind:this={cell[i][0]}
bind:value={name} />
{#each jobs.jobHours[i] as hour, j}
<input
type="number"
bind:this={cell[i][j+1]}
bind:value={hour}
/>
{/each}
<div>
{jobs.jobHours[i].reduce((acc,v)=>acc+=v)}
</div>
{/each}
</container>
Binding the prop in the parent component, allows the prop to be changed by the child component.在父组件中绑定 prop,允许子组件更改 prop。
App.svelte
<script>
import JobEntries from './JobEntries.svelte';
import Totals from './Totals.svelte';
let jobs = {
jobNames: ['Job1', 'Job2', 'Job3'],
jobHours: Array.from(Array(3), () => Array.from(Array(2), ()=>1))
};
</script>
<JobEntries bind:jobs/>
<Totals {jobs} />
JobEntries.svelte
<script>
export let jobs
let cell = Array.from(Array(3), () => Array.from(Array(3)));
</script>
<style>
container {
display: grid;
grid-template-columns: 2fr 1fr 1fr 1fr;
}
input, div {
width: 100%;
height: 100%;
min-width: 150px;
}
</style>
<container>
<div>
Name
</div>
<div>
Time 1
</div>
<div>
Time 2
</div>
<div>
Total
</div>
{#each jobs.jobNames as name, i}
<input
type="text"
bind:this={cell[i][0]}
bind:value={name} />
{#each jobs.jobHours[i] as hour, j}
<input
type="number"
bind:this={cell[i][j+1]}
bind:value={hour}
/>
{/each}
<div>
{jobs.jobHours[i].reduce((acc,v)=>acc+=v)}
</div>
{/each}
</container>
Alexander's comment got me thinking about jobs
in App.svelte
not actually being updated, and how I could accomplish that. Alexander 的评论让我想到了App.svelte
中的jobs
实际上并未更新,以及我如何才能做到这一点。 With Svelte's event dispatch
, I can have the child component dispatch the updated prop back to the parent component, and on receiving the message, update jobs
in App.svelte
.使用 Svelte 的event dispatch
,我可以让子组件将更新的 prop 发送回父组件,并在收到消息后更新App.svelte
中的jobs
。 Although I agree a Svelte store is probably a better way to handle state, this problem is solvable without a store.虽然我同意 Svelte 商店可能是处理 state 的更好方法,但这个问题可以在没有商店的情况下解决。 Below is the code.下面是代码。
App.svelte
<script>
import JobEntries from './JobEntries.svelte';
import Totals from './Totals.svelte';
const jobs = {
jobNames: ['Job1', 'Job2', 'Job3'],
jobHours: Array.from(Array(3), () => Array.from(Array(2), ()=>1))
};
</script>
<JobEntries {jobs} on:message={e=>jobs.jobHours = e.detail}/>
<Totals {jobs} />
JobEntries.svelte
<script>
import { createEventDispatcher } from 'svelte';
export let jobs
const dispatch = createEventDispatcher();
let cell = Array.from(Array(3), () => Array.from(Array(3)));
</script>
<style>
container {
display: grid;
grid-template-columns: 2fr 1fr 1fr 1fr;
}
input, div {
width: 100%;
height: 100%;
min-width: 150px;
}
</style>
<container>
<div>
Name
</div>
<div>
Time 1
</div>
<div>
Time 2
</div>
<div>
Total
</div>
{#each jobs.jobNames as name, i}
<input
type="text"
bind:this={cell[i][0]}
bind:value={name} />
{#each jobs.jobHours[i] as hour, j}
<input
type="number"
bind:this={cell[i][j+1]}
bind:value={hour}
on:input={e=>dispatch('message', jobs.jobHours)}
/>
{/each}
<div>
{jobs.jobHours[i].reduce((acc,v)=>acc+=v)}
</div>
{/each}
</container>
Totals.svelte
<script>
export let jobs
//this creates a new array which is two indexes long for each of the columns
//then fills each array with the sum of column
$: totals = new Array(2).fill(null).map((v, i) => {
let total = 0;
jobs.jobHours.forEach((v) => {
total += Number(v[i]);
});
return total;
});
//this sums the total hours row to give the grand total
$: grandTotal = totals.reduce((acc, v) => (acc += v));
</script>
<style>
container {
display: grid;
grid-template-columns: 2fr 1fr 1fr 1fr;
}
div {
width: 100%;
height: 100%;
width: 150px;
}
</style>
<container>
<div>Total Hours</div>
{#each totals as total}
<div>
{total}
</div>
{/each}
<div>{grandTotal}</div>
</container>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.