knockout / knockout

Knockout makes it easier to create rich, responsive UIs with JavaScript
http://knockoutjs.com/
Other
10.43k stars 1.52k forks source link

foreach doubles last entries under certain circumstances #2594

Open dbogatz opened 2 years ago

dbogatz commented 2 years ago

See: https://codepen.io/coyer/pen/zYRRqyo?editors=1010

HTML:

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
<div data-bind="foreach: $root.list[$root.key()]">
  <div data-bind="text: $data"></div>
</div>

Script:

function AppViewModel() {
  this.key = ko.observable();
  this.list = {};
}

var vm = new AppViewModel();
ko.options.deferUpdates = true;
ko.applyBindings(vm);
setTimeout(()=>vm.list["test"] = ko.observableArray(["apple", "orange"]),100);
setTimeout(()=>vm.key("test"), 200);
setTimeout(()=>{
  vm.key.valueHasMutated();
  vm.list['test'](["cat", "dog", "mice", "bird"]);
}, 300);

Expected Output after 300ms: "cat", "dog", "mice", "bird" but is "cat", "dog", "mice", "bird", "mice", "bird"

But works with "ko.options.deferUpdates = false;" or without "vm.key.valueHasMutated();" before setting new list or new list has same length as old list.

Anyway - thanks for the good job on knockout!

dbogatz commented 2 years ago

Also somewhat weird:

If I have two foreach on my page only the first one will be affected.

miellaby commented 2 years ago

I've got a table component which duplicates lines for no reason. My component life cycle looks like your example. I'm happy you was able to pinpoint the issue in a simple example.

By the way, your demo link is broken in the issue description.

dbogatz commented 2 years ago

Thx, I fixed the link in my description and I try to avoid valueHasMutated for now on. In my project it was used to trigger some computed's who access other changed non-observable data; it seems to put them in an observable too would be a good choice anyway.

mbest commented 1 year ago

I think this will be fixed with c41c1c3547c5cc7d884c47b0db95dc414b65e763, but I'll double check.