Skip to content

Commit c737cbf

Browse files
Merge pull request #1051 from wagenet/fix-keep-latest-empty
Fix issue with keepLatest and initial values
2 parents 0f42b44 + 6e16ad1 commit c737cbf

File tree

3 files changed

+148
-1
lines changed

3 files changed

+148
-1
lines changed

ember-resources/src/util/keep-latest.ts

+18
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,29 @@ interface Options<T = unknown> {
7070
export function keepLatest<Return = unknown>({ when, value: valueFn }: Options<Return>) {
7171
return resource(() => {
7272
let previous: Return;
73+
let initial = true;
7374

7475
return () => {
7576
let value = valueFn();
7677

7778
if (when()) {
79+
/**
80+
* Initially, if we may as well return the value instead
81+
* of the "previous" value is there is no previous yet.
82+
*
83+
* We check against undefined, because that's what
84+
* `previous` is "initialized" to.
85+
*
86+
* And then we never enter this block again, because
87+
* we will have previous values in future invocations of this
88+
* Formula.
89+
*/
90+
if (previous === undefined && initial) {
91+
initial = false;
92+
93+
return value;
94+
}
95+
7896
return (previous = isEmpty(value) ? previous : value);
7997
}
8098

test-app/tests/utils/keep-latest/js-test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ module('Utils | keepLatest | js', function (hooks) {
3232

3333
let instance = new Test();
3434

35-
assert.strictEqual(instance.data, undefined);
35+
assert.strictEqual(instance.data, null);
3636

3737
await timeout(100);
3838

test-app/tests/utils/keep-latest/rendering-test.gts

+129
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { render, settled } from '@ember/test-helpers';
55
import { module, test } from 'qunit';
66
import { setupRenderingTest } from 'ember-qunit';
77

8+
import { use } from 'ember-resources';
89
import { trackedFunction } from 'ember-resources/util/function';
910
import { keepLatest } from 'ember-resources/util/keep-latest';
1011

@@ -56,4 +57,132 @@ module('Utils | keepLatest | rendering', function (hooks) {
5657
await timeout(40);
5758
assert.dom().hasText('2');
5859
});
60+
61+
test('if the previous value is not empty, and the current value is empty', async function (assert) {
62+
class Test {
63+
@tracked x: number[] | number = [];
64+
65+
// @use request = trackedFunction(async () => {
66+
request = trackedFunction(this, async () => {
67+
let value = this.x;
68+
69+
await timeout(10);
70+
71+
return value;
72+
});
73+
74+
@use data = keepLatest({
75+
when: () => this.request.isPending,
76+
value: () => this.request.value,
77+
})
78+
}
79+
80+
let instance = new Test();
81+
82+
await render(<template>{{JSON.stringify instance.data}}</template>);
83+
84+
assert.dom().hasText('[]');
85+
86+
instance.x = [1];
87+
await settled();
88+
assert.dom().hasText('[1]');
89+
90+
instance.x = [];
91+
await timeout(8);
92+
assert.dom().hasText('[1]');
93+
await settled();
94+
assert.dom().hasText('[]');
95+
96+
instance.x = 0;
97+
await timeout(8);
98+
assert.dom().hasText('[]');
99+
100+
await settled();
101+
assert.dom().hasText('0');
102+
103+
instance.x = [1];
104+
await timeout(8);
105+
assert.dom().hasText('0');
106+
107+
await settled();
108+
assert.dom().hasText('[1]');
109+
110+
instance.x = [1, 2];
111+
assert.dom().hasText('[1]');
112+
await timeout(8);
113+
assert.dom().hasText('[1]');
114+
115+
await settled();
116+
assert.dom().hasText('[1,2]');
117+
});
118+
119+
test('with a default value, if the previous value is not empty, and the current value is empty', async function (assert) {
120+
class Test {
121+
@tracked x: number[] | null | number = [];
122+
123+
// @use request = trackedFunction(async () => {
124+
request = trackedFunction(this, async () => {
125+
let value = this.x;
126+
127+
await timeout(10);
128+
129+
return value;
130+
});
131+
132+
@use latest = keepLatest({
133+
when: () => this.request.isPending,
134+
value: () => this.request.value,
135+
})
136+
137+
get data() {
138+
return this.latest ?? 'default';
139+
}
140+
}
141+
142+
let instance = new Test();
143+
144+
render(<template>{{JSON.stringify instance.data}}</template>);
145+
146+
await timeout(8);
147+
/**
148+
* Initially, a `trackedFunction` returns null.
149+
* we could craft a resource that returns something other than null,
150+
* initially, but null ?? 'default' is 'default'.
151+
*/
152+
assert.dom().hasText('"default"', 'pre-settled, value exists ');
153+
await settled();
154+
assert.dom().hasText('[]', 'value exists, and set explicitly');
155+
156+
instance.x = [1];
157+
await settled();
158+
assert.dom().hasText('[1]', 'non-empty value exists and set explicitly');
159+
160+
instance.x = [];
161+
await timeout(8);
162+
assert.dom().hasText('[1]', 'retains previous value of [1]');
163+
await settled();
164+
assert.dom().hasText('[]', 'value resolved to empty []');
165+
166+
instance.x = null;
167+
await timeout(8);
168+
assert.dom().hasText('[]', 'retains previous value of []');
169+
170+
await settled();
171+
assert.dom().hasText('"default"', 'empty value set, falling back to default');
172+
173+
instance.x = [1];
174+
await timeout(8);
175+
assert.dom().hasText('"default"', 'not yet resolved, previous value used');
176+
177+
await settled();
178+
assert.dom().hasText('[1]', 'value resolved to [1]');
179+
180+
instance.x = [1, 2];
181+
assert.dom().hasText('[1]', 'retains previous non-empty value');
182+
await timeout(8);
183+
assert.dom().hasText('[1]', 'retains previous non-empty value');
184+
185+
await settled();
186+
assert.dom().hasText('[1,2]', 'new value is resolved');
187+
});
59188
});

0 commit comments

Comments
 (0)