Skip to content

Commit f04c9c3

Browse files
fix(compiler-vapor): selectors was not initialized in time when the initial value of createFor source was not empty (#13642)
1 parent d95fc18 commit f04c9c3

File tree

6 files changed

+53
-86
lines changed

6 files changed

+53
-86
lines changed

packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export function render(_ctx) {
4343
const n2 = t0()
4444
_setTemplateRef(n2, "foo", void 0, true)
4545
return n2
46-
}, null, 4)
46+
}, undefined, 4)
4747
return n0
4848
}"
4949
`;

packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export function render(_ctx) {
9393
const x4 = _child(n4)
9494
_renderEffect(() => _setText(x4, _toDisplayString(_for_item1.value+_for_item0.value)))
9595
return n4
96-
}, null, 1)
96+
}, undefined, 1)
9797
return n5
9898
})
9999
return n0
@@ -150,15 +150,17 @@ exports[`compiler: v-for > selector pattern 1`] = `
150150
const t0 = _template("<tr> </tr>", true)
151151
152152
export function render(_ctx) {
153+
let _selector0_0
153154
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
154155
const n2 = t0()
155156
const x2 = _child(n2)
156157
_selector0_0(() => {
157158
_setText(x2, _toDisplayString(_ctx.selected === _for_item0.value.id ? 'danger' : ''))
158159
})
159160
return n2
160-
}, (row) => (row.id))
161-
const _selector0_0 = n0.useSelector(() => _ctx.selected)
161+
}, (row) => (row.id), undefined, ({ createSelector }) => {
162+
_selector0_0 = createSelector(() => _ctx.selected)
163+
})
162164
return n0
163165
}"
164166
`;
@@ -168,14 +170,16 @@ exports[`compiler: v-for > selector pattern 2`] = `
168170
const t0 = _template("<tr></tr>", true)
169171
170172
export function render(_ctx) {
173+
let _selector0_0
171174
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
172175
const n2 = t0()
173176
_selector0_0(() => {
174177
_setClass(n2, _ctx.selected === _for_item0.value.id ? 'danger' : '')
175178
})
176179
return n2
177-
}, (row) => (row.id))
178-
const _selector0_0 = n0.useSelector(() => _ctx.selected)
180+
}, (row) => (row.id), undefined, ({ createSelector }) => {
181+
_selector0_0 = createSelector(() => _ctx.selected)
182+
})
179183
return n0
180184
}"
181185
`;
@@ -202,14 +206,16 @@ exports[`compiler: v-for > selector pattern 4`] = `
202206
const t0 = _template("<tr></tr>", true)
203207
204208
export function render(_ctx) {
209+
let _selector0_0
205210
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
206211
const n2 = t0()
207212
_selector0_0(() => {
208213
_setClass(n2, { danger: _for_item0.value.id === _ctx.selected })
209214
})
210215
return n2
211-
}, (row) => (row.id))
212-
const _selector0_0 = n0.useSelector(() => _ctx.selected)
216+
}, (row) => (row.id), undefined, ({ createSelector }) => {
217+
_selector0_0 = createSelector(() => _ctx.selected)
218+
})
213219
return n0
214220
}"
215221
`;

packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export function render(_ctx) {
6868
const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
6969
const n2 = t0()
7070
return n2
71-
}, null, 4)
71+
}, undefined, 4)
7272
return n0
7373
}"
7474
`;

packages/compiler-vapor/src/generators/for.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,19 +99,27 @@ export function genFor(
9999
keyProp,
100100
idMap,
101101
)
102-
const patternFrag: CodeFragment[] = []
102+
const selectorDeclarations: CodeFragment[] = []
103+
const selectorSetup: CodeFragment[] = []
103104

104105
for (let i = 0; i < selectorPatterns.length; i++) {
105106
const { selector } = selectorPatterns[i]
106107
const selectorName = `_selector${id}_${i}`
107-
patternFrag.push(
108+
selectorDeclarations.push(`let ${selectorName}`, NEWLINE)
109+
if (i === 0) {
110+
selectorSetup.push(`({ createSelector }) => {`, INDENT_START)
111+
}
112+
selectorSetup.push(
108113
NEWLINE,
109-
`const ${selectorName} = `,
110-
...genCall(`n${id}.useSelector`, [
114+
`${selectorName} = `,
115+
...genCall(`createSelector`, [
111116
`() => `,
112117
...genExpression(selector, context),
113118
]),
114119
)
120+
if (i === selectorPatterns.length - 1) {
121+
selectorSetup.push(INDENT_END, NEWLINE, '}')
122+
}
115123
}
116124

117125
const blockFn = context.withId(() => {
@@ -165,16 +173,17 @@ export function genFor(
165173

166174
return [
167175
NEWLINE,
176+
...selectorDeclarations,
168177
`const n${id} = `,
169178
...genCall(
170-
helper('createFor'),
179+
[helper('createFor'), 'undefined'],
171180
sourceExpr,
172181
blockFn,
173182
genCallback(keyProp),
174183
flags ? String(flags) : undefined,
184+
selectorSetup.length ? selectorSetup : undefined,
175185
// todo: hydrationNode
176186
),
177-
...patternFrag,
178187
]
179188

180189
// construct a id -> accessor path map.
Lines changed: 15 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { ref } from '@vue/reactivity'
22
import { makeRender } from './_utils'
3-
// @ts-expect-error
4-
import { createFor, createSelector, renderEffect } from '../src'
3+
import { createFor } from '../src'
54
import { nextTick } from '@vue/runtime-dom'
65

76
const define = makeRender()
87

9-
describe.todo('api: createSelector', () => {
8+
describe('api: createSelector', () => {
109
test('basic', async () => {
1110
let calledTimes = 0
1211
let expectedCalledTimes = 0
@@ -15,19 +14,23 @@ describe.todo('api: createSelector', () => {
1514
const index = ref(0)
1615

1716
const { host } = define(() => {
18-
const isSleected = createSelector(index)
17+
let selector: (cb: () => void) => void
1918
return createFor(
2019
() => list.value,
2120
item => {
2221
const span = document.createElement('li')
23-
renderEffect(() => {
22+
selector(() => {
2423
calledTimes += 1
2524
const { id } = item.value
26-
span.textContent = `${id}.${isSleected(id) ? 't' : 'f'}`
25+
span.textContent = `${id}.${id === index.value ? 't' : 'f'}`
2726
})
2827
return span
2928
},
3029
item => item.id,
30+
undefined,
31+
({ createSelector }) => {
32+
selector = createSelector(() => index.value)
33+
},
3134
)
3235
}).render()
3336

@@ -50,66 +53,11 @@ describe.todo('api: createSelector', () => {
5053
)
5154
expect(calledTimes).toBe((expectedCalledTimes += 2))
5255

53-
list.value[2].id = 3
54-
await nextTick()
55-
expect(host.innerHTML).toBe(
56-
'<li>0.f</li><li>1.f</li><li>3.f</li><!--for-->',
57-
)
58-
expect(calledTimes).toBe((expectedCalledTimes += 1))
59-
})
60-
61-
test('custom compare', async () => {
62-
let calledTimes = 0
63-
let expectedCalledTimes = 0
64-
65-
const list = ref([{ id: 1 }, { id: 2 }, { id: 3 }])
66-
const index = ref(0)
67-
68-
const { host } = define(() => {
69-
const isSleected = createSelector(
70-
index,
71-
// @ts-expect-error
72-
(key, value) => key === value + 1,
73-
)
74-
return createFor(
75-
() => list.value,
76-
item => {
77-
const span = document.createElement('li')
78-
renderEffect(() => {
79-
calledTimes += 1
80-
const { id } = item.value
81-
span.textContent = `${id}.${isSleected(id) ? 't' : 'f'}`
82-
})
83-
return span
84-
},
85-
item => item.id,
86-
)
87-
}).render()
88-
89-
expect(host.innerHTML).toBe(
90-
'<li>1.t</li><li>2.f</li><li>3.f</li><!--for-->',
91-
)
92-
expect(calledTimes).toBe((expectedCalledTimes += 3))
93-
94-
index.value = 1
95-
await nextTick()
96-
expect(host.innerHTML).toBe(
97-
'<li>1.f</li><li>2.t</li><li>3.f</li><!--for-->',
98-
)
99-
expect(calledTimes).toBe((expectedCalledTimes += 2))
100-
101-
index.value = 2
102-
await nextTick()
103-
expect(host.innerHTML).toBe(
104-
'<li>1.f</li><li>2.f</li><li>3.t</li><!--for-->',
105-
)
106-
expect(calledTimes).toBe((expectedCalledTimes += 2))
107-
108-
list.value[2].id = 4
109-
await nextTick()
110-
expect(host.innerHTML).toBe(
111-
'<li>1.f</li><li>2.f</li><li>4.f</li><!--for-->',
112-
)
113-
expect(calledTimes).toBe((expectedCalledTimes += 1))
56+
// list.value[2].id = 3
57+
// await nextTick()
58+
// expect(host.innerHTML).toBe(
59+
// '<li>0.f</li><li>1.f</li><li>3.f</li><!--for-->',
60+
// )
61+
// expect(calledTimes).toBe((expectedCalledTimes += 1))
11462
})
11563
})

packages/runtime-vapor/src/apiCreateFor.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ export const createFor = (
7474
) => Block,
7575
getKey?: (item: any, key: any, index?: number) => any,
7676
flags = 0,
77+
setup?: (_: {
78+
createSelector: (source: () => any) => (cb: () => void) => void
79+
}) => void,
7780
): VaporFragment => {
7881
const _insertionParent = insertionParent
7982
const _insertionAnchor = insertionAnchor
@@ -402,6 +405,10 @@ export const createFor = (
402405
}
403406
}
404407

408+
if (setup) {
409+
setup({ createSelector })
410+
}
411+
405412
if (flags & VaporVForFlags.ONCE) {
406413
renderList()
407414
} else {
@@ -412,12 +419,9 @@ export const createFor = (
412419
insert(frag, _insertionParent, _insertionAnchor)
413420
}
414421

415-
// @ts-expect-error
416-
frag.useSelector = useSelector
417-
418422
return frag
419423

420-
function useSelector(source: () => any): (key: any, cb: () => void) => void {
424+
function createSelector(source: () => any): (cb: () => void) => void {
421425
let operMap = new Map<any, (() => void)[]>()
422426
let activeKey = source()
423427
let activeOpers: (() => void)[] | undefined

0 commit comments

Comments
 (0)