Skip to content

Commit 1dabf43

Browse files
committed
Implement experimental_useOptimisticState (#26740)
This adds an experimental hook tentatively called useOptimisticState. (The actual name needs some bikeshedding.) The headline feature is that you can use it to implement optimistic updates. If you set some optimistic state during a transition/action, the state will be automatically reverted once the transition completes. Another feature is that the optimistic updates will be continually rebased on top of the latest state. It's easiest to explain with examples; we'll publish documentation as the API gets closer to stabilizing. See tests for now. Technically the use cases for this hook are broader than just optimistic updates; you could use it implement any sort of "pending" state, such as the ones exposed by useTransition and useFormStatus. But we expect people will most often reach for this hook to implement the optimistic update pattern; simpler cases are covered by those other hooks. DiffTrain build for commit 491aec5.
1 parent 5f70437 commit 1dabf43

File tree

13 files changed

+603
-439
lines changed

13 files changed

+603
-439
lines changed

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-dev.js

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<a56cd14b45443ff19095298bed47faaf>>
10+
* @generated SignedSource<<11116a9322957150fb8ff12ea7d79467>>
1111
*/
1212

1313
'use strict';
@@ -7227,6 +7227,10 @@ function mountReducer(reducer, initialArg, init) {
72277227

72287228
function updateReducer(reducer, initialArg, init) {
72297229
var hook = updateWorkInProgressHook();
7230+
return updateReducerImpl(hook, currentHook, reducer);
7231+
}
7232+
7233+
function updateReducerImpl(hook, current, reducer) {
72307234
var queue = hook.queue;
72317235

72327236
if (queue === null) {
@@ -7235,10 +7239,9 @@ function updateReducer(reducer, initialArg, init) {
72357239
);
72367240
}
72377241

7238-
queue.lastRenderedReducer = reducer;
7239-
var current = currentHook; // The last rebase update that is NOT part of the base state.
7242+
queue.lastRenderedReducer = reducer; // The last rebase update that is NOT part of the base state.
72407243

7241-
var baseQueue = current.baseQueue; // The last pending update that hasn't been processed yet.
7244+
var baseQueue = hook.baseQueue; // The last pending update that hasn't been processed yet.
72427245

72437246
var pendingQueue = queue.pending;
72447247

@@ -7271,7 +7274,7 @@ function updateReducer(reducer, initialArg, init) {
72717274
if (baseQueue !== null) {
72727275
// We have a queue to process.
72737276
var first = baseQueue.next;
7274-
var newState = current.baseState;
7277+
var newState = hook.baseState;
72757278
var newBaseState = null;
72767279
var newBaseQueueFirst = null;
72777280
var newBaseQueueLast = null;
@@ -7296,6 +7299,7 @@ function updateReducer(reducer, initialArg, init) {
72967299
// update/state.
72977300
var clone = {
72987301
lane: updateLane,
7302+
revertLane: update.revertLane,
72997303
action: update.action,
73007304
hasEagerState: update.hasEagerState,
73017305
eagerState: update.eagerState,
@@ -7317,19 +7321,24 @@ function updateReducer(reducer, initialArg, init) {
73177321
);
73187322
markSkippedUpdateLanes(updateLane);
73197323
} else {
7320-
// This update does have sufficient priority.
7321-
if (newBaseQueueLast !== null) {
7322-
var _clone = {
7323-
// This update is going to be committed so we never want uncommit
7324-
// it. Using NoLane works because 0 is a subset of all bitmasks, so
7325-
// this will never be skipped by the check above.
7326-
lane: NoLane,
7327-
action: update.action,
7328-
hasEagerState: update.hasEagerState,
7329-
eagerState: update.eagerState,
7330-
next: null
7331-
};
7332-
newBaseQueueLast = newBaseQueueLast.next = _clone;
7324+
{
7325+
// This is not an optimistic update, and we're going to apply it now.
7326+
// But, if there were earlier updates that were skipped, we need to
7327+
// leave this update in the queue so it can be rebased later.
7328+
if (newBaseQueueLast !== null) {
7329+
var _clone = {
7330+
// This update is going to be committed so we never want uncommit
7331+
// it. Using NoLane works because 0 is a subset of all bitmasks, so
7332+
// this will never be skipped by the check above.
7333+
lane: NoLane,
7334+
revertLane: NoLane,
7335+
action: update.action,
7336+
hasEagerState: update.hasEagerState,
7337+
eagerState: update.eagerState,
7338+
next: null
7339+
};
7340+
newBaseQueueLast = newBaseQueueLast.next = _clone;
7341+
}
73337342
} // Process this update.
73347343

73357344
var action = update.action;
@@ -7650,7 +7659,7 @@ function forceStoreRerender(fiber) {
76507659
}
76517660
}
76527661

7653-
function mountState(initialState) {
7662+
function mountStateImpl(initialState) {
76547663
var hook = mountWorkInProgressHook();
76557664

76567665
if (typeof initialState === "function") {
@@ -7667,11 +7676,14 @@ function mountState(initialState) {
76677676
lastRenderedState: initialState
76687677
};
76697678
hook.queue = queue;
7670-
var dispatch = (queue.dispatch = dispatchSetState.bind(
7671-
null,
7672-
currentlyRenderingFiber$1,
7673-
queue
7674-
));
7679+
return hook;
7680+
}
7681+
7682+
function mountState(initialState) {
7683+
var hook = mountStateImpl(initialState);
7684+
var queue = hook.queue;
7685+
var dispatch = dispatchSetState.bind(null, currentlyRenderingFiber$1, queue);
7686+
queue.dispatch = dispatch;
76757687
return [hook.memoizedState, dispatch];
76767688
}
76777689

@@ -8030,9 +8042,10 @@ function updateDeferredValueImpl(hook, prevValue, value) {
80308042
}
80318043

80328044
function startTransition(
8045+
fiber,
8046+
queue,
80338047
pendingState,
80348048
finishedState,
8035-
setPending,
80368049
callback,
80378050
options
80388051
) {
@@ -8041,8 +8054,12 @@ function startTransition(
80418054
higherEventPriority(previousPriority, ContinuousEventPriority)
80428055
);
80438056
var prevTransition = ReactCurrentBatchConfig$2.transition;
8044-
ReactCurrentBatchConfig$2.transition = null;
8045-
setPending(pendingState);
8057+
8058+
{
8059+
ReactCurrentBatchConfig$2.transition = null;
8060+
dispatchSetState(fiber, queue, pendingState);
8061+
}
8062+
80468063
var currentTransition = (ReactCurrentBatchConfig$2.transition = {});
80478064

80488065
{
@@ -8054,7 +8071,7 @@ function startTransition(
80548071
if (enableAsyncActions);
80558072
else {
80568073
// Async actions are not enabled.
8057-
setPending(finishedState);
8074+
dispatchSetState(fiber, queue, finishedState);
80588075
callback();
80598076
}
80608077
} catch (error) {
@@ -8086,10 +8103,15 @@ function startTransition(
80868103
}
80878104

80888105
function mountTransition() {
8089-
var _mountState = mountState(false),
8090-
setPending = _mountState[1]; // The `start` method never changes.
8106+
var stateHook = mountStateImpl(false); // The `start` method never changes.
80918107

8092-
var start = startTransition.bind(null, true, false, setPending);
8108+
var start = startTransition.bind(
8109+
null,
8110+
currentlyRenderingFiber$1,
8111+
stateHook.queue,
8112+
true,
8113+
false
8114+
);
80938115
var hook = mountWorkInProgressHook();
80948116
hook.memoizedState = start;
80958117
return [false, start];
@@ -8222,6 +8244,7 @@ function dispatchReducerAction(fiber, queue, action) {
82228244
var lane = requestUpdateLane(fiber);
82238245
var update = {
82248246
lane: lane,
8247+
revertLane: NoLane,
82258248
action: action,
82268249
hasEagerState: false,
82278250
eagerState: null,
@@ -8254,6 +8277,7 @@ function dispatchSetState(fiber, queue, action) {
82548277
var lane = requestUpdateLane(fiber);
82558278
var update = {
82568279
lane: lane,
8280+
revertLane: NoLane,
82578281
action: action,
82588282
hasEagerState: false,
82598283
eagerState: null,
@@ -23894,7 +23918,7 @@ function createFiberRoot(
2389423918
return root;
2389523919
}
2389623920

23897-
var ReactVersion = "18.3.0-next-9545e4810-20230501";
23921+
var ReactVersion = "18.3.0-next-491aec5d6-20230501";
2389823922

2389923923
// Might add PROFILE later.
2390023924

0 commit comments

Comments
 (0)