Skip to content

Commit 82d9dbe

Browse files
committed
Add expertimental optimisticKey behind a flag (facebook#35162)
When dealing with optimistic state, a common problem is not knowing the id of the thing we're waiting on. Items in lists need keys (and single items should often have keys too to reset their state). As a result you have to generate fake keys. It's a pain to manage those and when the real item comes in, you often end up rendering that with a different `key` which resets the state of the component tree. That in turns works against the grain of React and a lot of negatives fall out of it. This adds a special `optimisticKey` symbol that can be used in place of a `string` key. ```js import {optimisticKey} from 'react'; ... const [optimisticItems, setOptimisticItems] = useOptimistic([]); const children = savedItems.concat( optimisticItems.map(item => <Item key={optimisticKey} item={item} /> ) ); return <div>{children}</div>; ``` The semantics of this `optimisticKey` is that the assumption is that the newly saved item will be rendered in the same slot as the previous optimistic items. State is transferred into whatever real key ends up in the same slot. This might lead to some incorrect transferring of state in some cases where things don't end up lining up - but it's worth it for simplicity in many cases since dealing with true matching of optimistic state is often very complex for something that only lasts a blink of an eye. If a new item matches a `key` elsewhere in the set, then that's favored over reconciling against the old slot. One quirk with the current algorithm is if the `savedItems` has items removed, then the slots won't line up by index anymore and will be skewed. We might be able to add something where the optimistic set is always reconciled against the end. However, it's probably better to just assume that the set will line up perfectly and otherwise it's just best effort that can lead to weird artifacts. An `optimisticKey` will match itself for updates to the same slot, but it will not match any existing slot that is not an `optimisticKey`. So it's not an `any`, which I originally called it, because it doesn't match existing real keys against new optimistic keys. Only one direction. DiffTrain build for [eb89912](facebook@eb89912)
1 parent cf34a8a commit 82d9dbe

37 files changed

+509
-371
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0972e23908b573faa5beebc0eb1154f792c53b62
1+
eb89912ee5ace8bf8e616cca5a6aeebcd274b521
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0972e23908b573faa5beebc0eb1154f792c53b62
1+
eb89912ee5ace8bf8e616cca5a6aeebcd274b521

compiled/facebook-www/React-dev.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1499,7 +1499,7 @@ __DEV__ &&
14991499
exports.useTransition = function () {
15001500
return resolveDispatcher().useTransition();
15011501
};
1502-
exports.version = "19.3.0-www-classic-0972e239-20251118";
1502+
exports.version = "19.3.0-www-classic-eb89912e-20251118";
15031503
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
15041504
"function" ===
15051505
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-dev.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1499,7 +1499,7 @@ __DEV__ &&
14991499
exports.useTransition = function () {
15001500
return resolveDispatcher().useTransition();
15011501
};
1502-
exports.version = "19.3.0-www-modern-0972e239-20251118";
1502+
exports.version = "19.3.0-www-modern-eb89912e-20251118";
15031503
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
15041504
"function" ===
15051505
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-prod.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,4 +606,4 @@ exports.useSyncExternalStore = function (
606606
exports.useTransition = function () {
607607
return ReactSharedInternals.H.useTransition();
608608
};
609-
exports.version = "19.3.0-www-classic-0972e239-20251118";
609+
exports.version = "19.3.0-www-classic-eb89912e-20251118";

compiled/facebook-www/React-prod.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,4 +606,4 @@ exports.useSyncExternalStore = function (
606606
exports.useTransition = function () {
607607
return ReactSharedInternals.H.useTransition();
608608
};
609-
exports.version = "19.3.0-www-modern-0972e239-20251118";
609+
exports.version = "19.3.0-www-modern-eb89912e-20251118";

compiled/facebook-www/React-profiling.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ exports.useSyncExternalStore = function (
610610
exports.useTransition = function () {
611611
return ReactSharedInternals.H.useTransition();
612612
};
613-
exports.version = "19.3.0-www-classic-0972e239-20251118";
613+
exports.version = "19.3.0-www-classic-eb89912e-20251118";
614614
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
615615
"function" ===
616616
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-profiling.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ exports.useSyncExternalStore = function (
610610
exports.useTransition = function () {
611611
return ReactSharedInternals.H.useTransition();
612612
};
613-
exports.version = "19.3.0-www-modern-0972e239-20251118";
613+
exports.version = "19.3.0-www-modern-eb89912e-20251118";
614614
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
615615
"function" ===
616616
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/ReactART-dev.classic.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4065,9 +4065,9 @@ __DEV__ &&
40654065
}
40664066
function mapRemainingChildren(currentFirstChild) {
40674067
for (var existingChildren = new Map(); null !== currentFirstChild; )
4068-
null !== currentFirstChild.key
4069-
? existingChildren.set(currentFirstChild.key, currentFirstChild)
4070-
: existingChildren.set(currentFirstChild.index, currentFirstChild),
4068+
null === currentFirstChild.key
4069+
? existingChildren.set(currentFirstChild.index, currentFirstChild)
4070+
: existingChildren.set(currentFirstChild.key, currentFirstChild),
40714071
(currentFirstChild = currentFirstChild.sibling);
40724072
return existingChildren;
40734073
}
@@ -4584,10 +4584,11 @@ __DEV__ &&
45844584
knownKeys
45854585
)),
45864586
shouldTrackSideEffects &&
4587-
null !== nextOldFiber.alternate &&
4588-
oldFiber.delete(
4589-
null === nextOldFiber.key ? newIdx : nextOldFiber.key
4590-
),
4587+
((newFiber = nextOldFiber.alternate),
4588+
null !== newFiber &&
4589+
oldFiber.delete(
4590+
null === newFiber.key ? newIdx : newFiber.key
4591+
)),
45914592
(currentFirstChild = placeChild(
45924593
nextOldFiber,
45934594
currentFirstChild,
@@ -4692,10 +4693,9 @@ __DEV__ &&
46924693
knownKeys
46934694
)),
46944695
shouldTrackSideEffects &&
4695-
null !== nextOldFiber.alternate &&
4696-
oldFiber.delete(
4697-
null === nextOldFiber.key ? newIdx : nextOldFiber.key
4698-
),
4696+
((step = nextOldFiber.alternate),
4697+
null !== step &&
4698+
oldFiber.delete(null === step.key ? newIdx : step.key)),
46994699
(currentFirstChild = placeChild(
47004700
nextOldFiber,
47014701
currentFirstChild,
@@ -20466,10 +20466,10 @@ __DEV__ &&
2046620466
(function () {
2046720467
var internals = {
2046820468
bundleType: 1,
20469-
version: "19.3.0-www-classic-0972e239-20251118",
20469+
version: "19.3.0-www-classic-eb89912e-20251118",
2047020470
rendererPackageName: "react-art",
2047120471
currentDispatcherRef: ReactSharedInternals,
20472-
reconcilerVersion: "19.3.0-www-classic-0972e239-20251118"
20472+
reconcilerVersion: "19.3.0-www-classic-eb89912e-20251118"
2047320473
};
2047420474
internals.overrideHookState = overrideHookState;
2047520475
internals.overrideHookStateDeletePath = overrideHookStateDeletePath;
@@ -20504,7 +20504,7 @@ __DEV__ &&
2050420504
exports.Shape = Shape;
2050520505
exports.Surface = Surface;
2050620506
exports.Text = Text;
20507-
exports.version = "19.3.0-www-classic-0972e239-20251118";
20507+
exports.version = "19.3.0-www-classic-eb89912e-20251118";
2050820508
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
2050920509
"function" ===
2051020510
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/ReactART-dev.modern.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3971,9 +3971,9 @@ __DEV__ &&
39713971
}
39723972
function mapRemainingChildren(currentFirstChild) {
39733973
for (var existingChildren = new Map(); null !== currentFirstChild; )
3974-
null !== currentFirstChild.key
3975-
? existingChildren.set(currentFirstChild.key, currentFirstChild)
3976-
: existingChildren.set(currentFirstChild.index, currentFirstChild),
3974+
null === currentFirstChild.key
3975+
? existingChildren.set(currentFirstChild.index, currentFirstChild)
3976+
: existingChildren.set(currentFirstChild.key, currentFirstChild),
39773977
(currentFirstChild = currentFirstChild.sibling);
39783978
return existingChildren;
39793979
}
@@ -4490,10 +4490,11 @@ __DEV__ &&
44904490
knownKeys
44914491
)),
44924492
shouldTrackSideEffects &&
4493-
null !== nextOldFiber.alternate &&
4494-
oldFiber.delete(
4495-
null === nextOldFiber.key ? newIdx : nextOldFiber.key
4496-
),
4493+
((newFiber = nextOldFiber.alternate),
4494+
null !== newFiber &&
4495+
oldFiber.delete(
4496+
null === newFiber.key ? newIdx : newFiber.key
4497+
)),
44974498
(currentFirstChild = placeChild(
44984499
nextOldFiber,
44994500
currentFirstChild,
@@ -4598,10 +4599,9 @@ __DEV__ &&
45984599
knownKeys
45994600
)),
46004601
shouldTrackSideEffects &&
4601-
null !== nextOldFiber.alternate &&
4602-
oldFiber.delete(
4603-
null === nextOldFiber.key ? newIdx : nextOldFiber.key
4604-
),
4602+
((step = nextOldFiber.alternate),
4603+
null !== step &&
4604+
oldFiber.delete(null === step.key ? newIdx : step.key)),
46054605
(currentFirstChild = placeChild(
46064606
nextOldFiber,
46074607
currentFirstChild,
@@ -20237,10 +20237,10 @@ __DEV__ &&
2023720237
(function () {
2023820238
var internals = {
2023920239
bundleType: 1,
20240-
version: "19.3.0-www-modern-0972e239-20251118",
20240+
version: "19.3.0-www-modern-eb89912e-20251118",
2024120241
rendererPackageName: "react-art",
2024220242
currentDispatcherRef: ReactSharedInternals,
20243-
reconcilerVersion: "19.3.0-www-modern-0972e239-20251118"
20243+
reconcilerVersion: "19.3.0-www-modern-eb89912e-20251118"
2024420244
};
2024520245
internals.overrideHookState = overrideHookState;
2024620246
internals.overrideHookStateDeletePath = overrideHookStateDeletePath;
@@ -20275,7 +20275,7 @@ __DEV__ &&
2027520275
exports.Shape = Shape;
2027620276
exports.Surface = Surface;
2027720277
exports.Text = Text;
20278-
exports.version = "19.3.0-www-modern-0972e239-20251118";
20278+
exports.version = "19.3.0-www-modern-eb89912e-20251118";
2027920279
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
2028020280
"function" ===
2028120281
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

0 commit comments

Comments
 (0)