Skip to content

feat(runtime-core): useId() #11404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
refactor: fix deep cases
  • Loading branch information
yyx990803 committed Jul 19, 2024
commit f197b51631a8b0f834cca2aeb40988efda9bcfeb
68 changes: 56 additions & 12 deletions packages/runtime-core/__tests__/helpers/useId.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,17 @@ describe('useId', () => {
const app = createApp(BasicComponentWithUseId)
return [app, []]
}),
).toBe('v0:0 v0:1')
).toBe('v:0 v:1')
})

test('with config.idPrefix', async () => {
expect(
await getOutput(() => {
const app = createApp(BasicComponentWithUseId)
app.config.idPrefix = 'foo-'
app.config.idPrefix = 'foo'
return [app, []]
}),
).toBe('foo-0:0 foo-0:1')
).toBe('foo:0 foo:1')
})

test('async component', async () => {
Expand All @@ -91,9 +91,9 @@ describe('useId', () => {
}

const expected =
'v0:0 v0:1 ' + // root
'v1:0 v1:1 ' + // inside first async subtree
'v2:0 v2:1' // inside second async subtree
'v:0 v:1 ' + // root
'v:0-0 v:0-1 ' + // inside first async subtree
'v:1-0 v:1-1' // inside second async subtree
// assert different async resolution order does not affect id stable-ness
expect(await getOutput(() => factory(10, 20))).toBe(expected)
expect(await getOutput(() => factory(20, 10))).toBe(expected)
Expand Down Expand Up @@ -136,9 +136,9 @@ describe('useId', () => {
}

const expected =
'v0:0 v0:1 ' + // root
'v1:0 v1:1 ' + // inside first async subtree
'v2:0 v2:1' // inside second async subtree
'v:0 v:1 ' + // root
'v:0-0 v:0-1 ' + // inside first async subtree
'v:1-0 v:1-1' // inside second async subtree
// assert different async resolution order does not affect id stable-ness
expect(await getOutput(() => factory(10, 20))).toBe(expected)
expect(await getOutput(() => factory(20, 10))).toBe(expected)
Expand Down Expand Up @@ -187,12 +187,56 @@ describe('useId', () => {

const expected =
'<div>' +
'v0:0 v0:1 ' + // root
'v1:0 v1:1 ' + // inside first async subtree
'v2:0 v2:1' + // inside second async subtree
'v:0 v:1 ' + // root
'v:0-0 v:0-1 ' + // inside first async subtree
'v:1-0 v:1-1' + // inside second async subtree
'</div>'
// assert different async resolution order does not affect id stable-ness
expect(await getOutput(() => factory(10, 20))).toBe(expected)
expect(await getOutput(() => factory(20, 10))).toBe(expected)
})

test('deep nested', async () => {
const factory = (): ReturnType<TestCaseFactory> => {
const p = Promise.resolve()
const One = {
async setup() {
const id = useId()
await p
return () => [id, ' ', h(Two), ' ', h(Three)]
},
}
const Two = {
async setup() {
const id = useId()
await p
return () => [id, ' ', h(Three), ' ', h(Three)]
},
}
const Three = {
async setup() {
const id = useId()
return () => id
},
}
const app = createApp({
setup() {
return () =>
h(Suspense, null, {
default: h(One),
})
},
})
return [app, [p]]
}

const expected =
'v:0 ' + // One
'v:0-0 ' + // Two
'v:0-0-0 v:0-0-1 ' + // Three + Three nested in Two
'v:0-1' // Three after Two
// assert different async resolution order does not affect id stable-ness
expect(await getOutput(() => factory())).toBe(expected)
expect(await getOutput(() => factory())).toBe(expected)
})
})
7 changes: 3 additions & 4 deletions packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,12 +359,11 @@ export interface ComponentInternalInstance {
provides: Data
/**
* for tracking useId()
* first number is the index of the current async boundrary
* first element is the current boundary prefix
* second number is the index of the useId call within that boundary
* third number is the count of child async boundaries
* @internal
*/
ids: [number, number, number]
ids: [string, number, number]
/**
* Tracking reactive effects (e.g. watchers) associated with this component
* so that they can be automatically stopped on component unmount
Expand Down Expand Up @@ -628,7 +627,7 @@ export function createComponentInstance(
withProxy: null,

provides: parent ? parent.provides : Object.create(appContext.provides),
ids: parent ? parent.ids : [0, 0, 0],
ids: parent ? parent.ids : ['', 0, 0],
accessCache: null!,
renderCache: [],

Expand Down
4 changes: 2 additions & 2 deletions packages/runtime-core/src/helpers/useId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { warn } from '../warning'
export function useId() {
const i = getCurrentInstance()
if (i) {
return (i.appContext.config.idPrefix || 'v') + i.ids[0] + ':' + i.ids[1]++
return (i.appContext.config.idPrefix || 'v') + ':' + i.ids[0] + i.ids[1]++
} else if (__DEV__) {
warn(
`useId() is called when there is no active component ` +
Expand All @@ -23,5 +23,5 @@ export function useId() {
* - components with serverPrefetch
*/
export function markAsyncBoundary(instance: ComponentInternalInstance) {
instance.ids = [++instance.ids[2], 0, 0]
instance.ids = [instance.ids[0] + instance.ids[2]++ + '-', 0, 0]
}