Skip to content
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

useId hook #3402

Closed
wants to merge 19 commits into from
Closed

useId hook #3402

wants to merge 19 commits into from

Conversation

JoviDeCroock
Copy link
Member

@JoviDeCroock JoviDeCroock commented Jan 9, 2022

Implements #3373

Currently this hook leverages a combination of the component _vnodeId (ensures uniqueness across switching subtrees and roots), the vnode depth (ensures uniqueness within a subtree) and the order of the hook.

EDIT1:
Replacing the usage of _vnodeId and _depth with a root-identifier and the dom-depth because in SSR currently there are a lot of cases where a subtree isn't rendered until we're on the client due to it being personal information this could skew _vnodeId in our current implementation. Same goes for _depth if SSR has an additional or missing FunctionalComponent the _depth will be erroneous, we avoid missmatches during hydration so _domDepth is far more reliable.

This adds tracking for a rootId and _domDepth

TODO: uniqueness across siblings, this could prove troublesome if siblings are fenced with Fragments

Parent
  Fragment -- Child -- useId();
  Fragment -- Child -- useId();

In this example both Child's will see a _domDepth of 0 and a parentPosition of 0 so this could produce the same id which is erroneous.

EDIT2: replaced the counter that counted the amount of useId invocations scoped to a vnode to be a global counter which will bypass the issue outlined above.

EDIT3: the uniqueness across siblings has been caught by looking at the index the child has inside of the parentInternal._children, we could now scope the counter to be by vnode to avoid big numbers if we incorporate something like a _domDepth unless we want to assume _depth to be consistent

This is currently blocked by changes needed to RenderToString

@coveralls
Copy link

coveralls commented Jan 9, 2022

Coverage Status

Coverage increased (+0.002%) to 99.437% when pulling 3175c73 on use-id-hook into ac88f1f on main.

@github-actions
Copy link

github-actions bot commented Jan 9, 2022

Size Change: +316 B (0%)

Total Size: 38.6 kB

Filename Size Change
dist/preact.js 4.64 kB -25 B (0%)
dist/preact.min.js 4.7 kB -21 B (0%)
dist/preact.umd.js 4.71 kB -21 B (0%)
hooks/dist/hooks.js 1.39 kB +63 B (4%)
hooks/dist/hooks.umd.js 1.47 kB +61 B (4%)
server/dist/server.js 2.73 kB +129 B (4%)
server/dist/server.umd.js 2.82 kB +130 B (4%)
ℹ️ View Unchanged
Filename Size Change
compat/dist/compat.js 3.63 kB 0 B
compat/dist/compat.umd.js 3.71 kB 0 B
debug/dist/debug.js 3.25 kB 0 B
debug/dist/debug.umd.js 3.33 kB 0 B
devtools/dist/devtools.js 232 B 0 B
devtools/dist/devtools.umd.js 316 B 0 B
jsx-runtime/dist/jsxRuntime.js 342 B 0 B
jsx-runtime/dist/jsxRuntime.umd.js 425 B 0 B
test-utils/dist/testUtils.js 431 B 0 B
test-utils/dist/testUtils.umd.js 516 B 0 B

compressed-size-action

hooks/src/index.js Outdated Show resolved Hide resolved
@github-actions
Copy link

github-actions bot commented Jan 10, 2022

📊 Tachometer Benchmark Results

Summary

duration

  • 02_replace1k: unsure 🔍 -1% - +1% (-0.56ms - +0.55ms)
    preact-local vs preact-master
  • 02_replace1k: unsure 🔍 -2% - +1% (-2.49ms - +1.12ms)
    preact-local vs preact-master
  • 03_update10th1k_x16: unsure 🔍 -2% - +4% (-1.01ms - +2.25ms)
    preact-local vs preact-master
  • 07_create10k: unsure 🔍 -0% - +1% (-7.09ms - +20.71ms)
    preact-local vs preact-master
  • filter_list: unsure 🔍 -1% - +6% (-5.96ms - +58.57ms)
    preact-local vs preact-master
  • hydrate1k: unsure 🔍 -4% - +15% (-3.40ms - +14.48ms)
    preact-local vs preact-master
  • many_updates: unsure 🔍 -6% - +3% (-43.88ms - +19.47ms)
    preact-local vs preact-master
  • text_update: unsure 🔍 -9% - +3% (-34.87ms - +10.35ms)
    preact-local vs preact-master
  • todo: unsure 🔍 -3% - +3% (-1.39ms - +1.11ms)
    preact-local vs preact-master

usedJSHeapSize

  • 02_replace1k: slower ❌ 2% - 2% (0.09ms - 0.10ms)
    preact-local vs preact-master
  • 02_replace1k: unsure 🔍 -0% - +0% (-0.01ms - +0.01ms)
    preact-local vs preact-master
  • 03_update10th1k_x16: unsure 🔍 -0% - +0% (-0.00ms - +0.01ms)
    preact-local vs preact-master
  • 07_create10k: unsure 🔍 -2% - +3% (-0.03ms - +0.04ms)
    preact-local vs preact-master
  • filter_list: unsure 🔍 -0% - +1% (-0.00ms - +0.01ms)
    preact-local vs preact-master
  • hydrate1k: unsure 🔍 -0% - +0% (-0.00ms - +0.00ms)
    preact-local vs preact-master
  • many_updates: unsure 🔍 -0% - +0% (-0.00ms - +0.00ms)
    preact-local vs preact-master
  • text_update: unsure 🔍 -0% - +0% (-0.00ms - +0.00ms)
    preact-local vs preact-master
  • todo: unsure 🔍 -2% - +3% (-0.03ms - +0.04ms)
    preact-local vs preact-master

Results

02_replace1k

duration

VersionAvg timevs preact-mastervs preact-local
preact-master105.28ms - 106.00ms-unsure 🔍
-1% - +1%
-0.55ms - +0.56ms
preact-local105.22ms - 106.05msunsure 🔍
-1% - +1%
-0.56ms - +0.55ms
-

usedJSHeapSize

VersionAvg timevs preact-mastervs preact-local
preact-master4.06ms - 4.07ms-faster ✔
2% - 2%
0.09ms - 0.10ms
preact-local4.16ms - 4.16msslower ❌
2% - 2%
0.09ms - 0.10ms
-

run-warmup-0

VersionAvg timevs preact-mastervs preact-local
preact-master49.85ms - 51.04ms-unsure 🔍
-3% - +0%
-1.52ms - +0.01ms
preact-local50.72ms - 51.68msunsure 🔍
-0% - +3%
-0.01ms - +1.52ms
-

run-warmup-1

VersionAvg timevs preact-mastervs preact-local
preact-master60.74ms - 62.59ms-unsure 🔍
-1% - +3%
-0.86ms - +1.62ms
preact-local60.46ms - 62.11msunsure 🔍
-3% - +1%
-1.62ms - +0.86ms
-

run-warmup-2

VersionAvg timevs preact-mastervs preact-local
preact-master52.65ms - 53.74ms-slower ❌
3% - 8%
1.53ms - 3.75ms
preact-local49.59ms - 51.53msfaster ✔
3% - 7%
1.53ms - 3.75ms
-

run-warmup-3

VersionAvg timevs preact-mastervs preact-local
preact-master39.90ms - 41.16ms-unsure 🔍
-2% - +2%
-0.84ms - +0.79ms
preact-local40.03ms - 41.08msunsure 🔍
-2% - +2%
-0.79ms - +0.84ms
-

run-warmup-4

VersionAvg timevs preact-mastervs preact-local
preact-master56.00ms - 58.75ms-faster ✔
0% - 6%
0.17ms - 3.44ms
preact-local58.30ms - 60.06msslower ❌
0% - 6%
0.17ms - 3.44ms
-

run-final

VersionAvg timevs preact-mastervs preact-local
preact-master105.33ms - 106.04ms-unsure 🔍
-1% - +1%
-0.55ms - +0.56ms
preact-local105.26ms - 106.10msunsure 🔍
-1% - +1%
-0.56ms - +0.55ms
-
02_replace1k

duration

VersionAvg timevs preact-mastervs preact-local
preact-master141.97ms - 144.95ms-unsure 🔍
-1% - +2%
-1.12ms - +2.49ms
preact-local141.77ms - 143.80msunsure 🔍
-2% - +1%
-2.49ms - +1.12ms
-

usedJSHeapSize

VersionAvg timevs preact-mastervs preact-local
preact-master4.17ms - 4.19ms-unsure 🔍
-0% - +0%
-0.01ms - +0.01ms
preact-local4.18ms - 4.19msunsure 🔍
-0% - +0%
-0.01ms - +0.01ms
-

run-warmup-0

VersionAvg timevs preact-mastervs preact-local
preact-master57.49ms - 59.49ms-unsure 🔍
-2% - +3%
-1.20ms - +1.61ms
preact-local57.30ms - 59.28msunsure 🔍
-3% - +2%
-1.61ms - +1.20ms
-

run-warmup-1

VersionAvg timevs preact-mastervs preact-local
preact-master83.16ms - 87.49ms-unsure 🔍
-2% - +5%
-1.73ms - +4.58ms
preact-local81.60ms - 86.20msunsure 🔍
-5% - +2%
-4.58ms - +1.73ms
-

run-warmup-2

VersionAvg timevs preact-mastervs preact-local
preact-master68.75ms - 70.47ms-unsure 🔍
-3% - +2%
-2.02ms - +1.31ms
preact-local68.53ms - 71.38msunsure 🔍
-2% - +3%
-1.31ms - +2.02ms
-

run-final

VersionAvg timevs preact-mastervs preact-local
preact-master142.01ms - 144.99ms-unsure 🔍
-1% - +2%
-1.13ms - +2.49ms
preact-local141.80ms - 143.84msunsure 🔍
-2% - +1%
-2.49ms - +1.13ms
-
03_update10th1k_x16

duration

VersionAvg timevs preact-mastervs preact-local
preact-master53.66ms - 55.95ms-unsure 🔍
-4% - +2%
-2.25ms - +1.01ms
preact-local54.26ms - 56.59msunsure 🔍
-2% - +4%
-1.01ms - +2.25ms
-

usedJSHeapSize

VersionAvg timevs preact-mastervs preact-local
preact-master4.20ms - 4.21ms-unsure 🔍
-0% - +0%
-0.01ms - +0.00ms
preact-local4.20ms - 4.21msunsure 🔍
-0% - +0%
-0.00ms - +0.01ms
-
07_create10k

duration

VersionAvg timevs preact-mastervs preact-local
preact-master1797.47ms - 1817.81ms-unsure 🔍
-1% - +0%
-20.71ms - +7.09ms
preact-local1804.98ms - 1823.93msunsure 🔍
-0% - +1%
-7.09ms - +20.71ms
-

usedJSHeapSize

VersionAvg timevs preact-mastervs preact-local
preact-master1.40ms - 1.44ms-unsure 🔍
-3% - +2%
-0.04ms - +0.03ms
preact-local1.40ms - 1.45msunsure 🔍
-2% - +3%
-0.03ms - +0.04ms
-
filter_list

duration

VersionAvg timevs preact-mastervs preact-local
preact-master1048.04ms - 1096.82ms-unsure 🔍
-5% - +1%
-58.57ms - +5.96ms
preact-local1077.61ms - 1119.86msunsure 🔍
-1% - +6%
-5.96ms - +58.57ms
-

usedJSHeapSize

VersionAvg timevs preact-mastervs preact-local
preact-master2.03ms - 2.05ms-unsure 🔍
-1% - +0%
-0.01ms - +0.00ms
preact-local2.04ms - 2.05msunsure 🔍
-0% - +1%
-0.00ms - +0.01ms
-
hydrate1k

duration

VersionAvg timevs preact-mastervs preact-local
preact-master94.79ms - 106.25ms-unsure 🔍
-13% - +3%
-14.48ms - +3.40ms
preact-local99.20ms - 112.92msunsure 🔍
-4% - +15%
-3.40ms - +14.48ms
-

usedJSHeapSize

VersionAvg timevs preact-mastervs preact-local
preact-master6.90ms - 6.90ms-unsure 🔍
-0% - +0%
-0.00ms - +0.00ms
preact-local6.90ms - 6.90msunsure 🔍
-0% - +0%
-0.00ms - +0.00ms
-
many_updates

duration

VersionAvg timevs preact-mastervs preact-local
preact-master700.05ms - 748.40ms-unsure 🔍
-3% - +6%
-19.47ms - +43.88ms
preact-local691.55ms - 732.49msunsure 🔍
-6% - +3%
-43.88ms - +19.47ms
-

usedJSHeapSize

VersionAvg timevs preact-mastervs preact-local
preact-master5.70ms - 5.71ms-unsure 🔍
-0% - +0%
-0.00ms - +0.00ms
preact-local5.70ms - 5.71msunsure 🔍
-0% - +0%
-0.00ms - +0.00ms
-
text_update

duration

VersionAvg timevs preact-mastervs preact-local
preact-master361.06ms - 393.16ms-unsure 🔍
-3% - +10%
-10.35ms - +34.87ms
preact-local348.93ms - 380.77msunsure 🔍
-9% - +3%
-34.87ms - +10.35ms
-

usedJSHeapSize

VersionAvg timevs preact-mastervs preact-local
preact-master1.30ms - 1.30ms-unsure 🔍
-0% - +0%
-0.00ms - +0.00ms
preact-local1.30ms - 1.30msunsure 🔍
-0% - +0%
-0.00ms - +0.00ms
-
todo

duration

VersionAvg timevs preact-mastervs preact-local
preact-master42.74ms - 44.42ms-unsure 🔍
-3% - +3%
-1.11ms - +1.39ms
preact-local42.52ms - 44.37msunsure 🔍
-3% - +3%
-1.39ms - +1.11ms
-

usedJSHeapSize

VersionAvg timevs preact-mastervs preact-local
preact-master1.35ms - 1.40ms-unsure 🔍
-3% - +2%
-0.04ms - +0.03ms
preact-local1.36ms - 1.41msunsure 🔍
-2% - +3%
-0.03ms - +0.04ms
-

tachometer-reporter-action v2 for Benchmarks

@JoviDeCroock JoviDeCroock marked this pull request as ready for review January 10, 2022 15:04
@@ -11,6 +11,8 @@ import { mount } from './diff/mount';
import { patch } from './diff/patch';
import { createInternal } from './tree';

let rootId = 0;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to introduce some randomness here in case there are multiple completely separate Preact installations running, i.e. 2 widgets as those will be two roots with a resetting _domDepth

Copy link
Member

@marvinhagemeister marvinhagemeister Jan 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: In devtools I'm using this to sort of namespace the ids of different preact installations

// Create an integer-based namespace to avoid clashing ids caused by
// multiple connected renderers
const namespace = Math.floor(Math.random() * 2 ** 32);

https://github.com/preactjs/preact-devtools/blob/15762ba29ea1cfeca6964bb09dadf4dc625d2bde/src/adapter/hook.ts#L211

Copy link
Member

@ForsakenHarmony ForsakenHarmony Feb 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Randomness would break hydration with SSR though, no?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, good point about the randomness!

const state = getHookState(currentIndex++, 11);
if (!state._id) {
currentIdCounter++;
state._id = (currentInternal._rootId + currentInternal._domDepth + currentIdCounter).toString(32)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know how collision resistant this is?

Also, do we need to worry about preact users using a simple ID (single digit) themselves?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefixed with _P now

'_P' +
currentInternal._rootId +
(currentInternal._parent._children.indexOf(currentInternal) +
currentInternal._depth +
Copy link
Member Author

@JoviDeCroock JoviDeCroock Mar 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_domDepth might be more reliable here as sometimes people wrap their ssr with a wrapper for the routing, ... we could also just remove it as the global id counter should take care of it either way

We could even just do

			'_P' +
			currentInternal._rootId + currentIdCounter

The only thing that currently bothers me is the fact that this could be annoying when we talk about plugins, maybe we allow for custom prefixes to help them out?

Something like

export function useId(customPrefix) {
	const state = getHookState(currentIndex++, 11);
	if (!state._id) {
		currentIdCounter++;

		state._id =
			customPrefix || '_P' +
			currentInternal._rootId +
			currentIdCounter
	}
	return state._id;
}


state._id =
'_P' +
currentInternal._rootId +
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm rts should set this as well then

@dvoytenko
Copy link

Does this approach support Suspense?

@JoviDeCroock
Copy link
Member Author

@dvoytenko it does yes as it does not take any global counters, it will rely on the state of the subtree which in suspense is a stable thing to rely on.

A combination of a rootId, the position of the node in the parents children and the amount of useId invocations for a node. In React this implementation is a lot more complex as they bitshift forks of the tree I assume for concurrency issues

@dvoytenko
Copy link

Is it closed because there's an alternative PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants