76
prepareHostDispatcher,
77
supportsRequestStorage,
78
requestStorage,
79
+ pushFormStateMarkerIsMatching,
80
+ pushFormStateMarkerIsNotMatching,
81
} from './ReactFizzConfig';
82
import {
83
constructClassInstance,
106
setCurrentResumableState,
107
getThenableStateAfterSuspending,
108
unwrapThenable,
109
+ getFormStateCount,
110
+ getFormStateMatchingIndex,
111
} from './ReactFizzHooks';
112
import {DefaultCacheDispatcher} from './ReactFizzCache';
113
import {getStackByComponentStackNode} from './ReactFizzComponentStack';
1048
legacyContext,
1049
);
1050
const hasId = checkDidRenderIdHook();
1051
+ const formStateCount = getFormStateCount();
1052
+ const formStateMatchingIndex = getFormStateMatchingIndex();
1053
1054
if (__DEV__) {
1055
// Support for module components is deprecated and is removed behind a flag.
1119
if (__DEV__) {
1120
validateFunctionComponentInDev(Component);
1121
}
1116
- // We're now successfully past this task, and we don't have to pop back to
1117
- // the previous task every again, so we can use the destructive recursive form.
1118
- if (hasId) {
1119
- // This component materialized an id. We treat this as its own level, with
1120
- // a single "child" slot.
1121
- const prevTreeContext = task.treeContext;
1122
- const totalChildren = 1;
1123
- const index = 0;
1124
- // Modify the id context. Because we'll need to reset this if something
1125
- // suspends or errors, we'll use the non-destructive render path.
1126
- task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
1127
- renderNode(request, task, value, 0);
1128
- // Like the other contexts, this does not need to be in a finally block
1129
- // because renderNode takes care of unwinding the stack.
1130
- task.treeContext = prevTreeContext;
1122
+ finishFunctionComponent(
1123
+ request,
1124
+ task,
1125
+ value,
1126
+ hasId,
1127
+ formStateCount,
1128
+ formStateMatchingIndex,
1129
+ );
1130
+ }
1131
+ popComponentStackInDEV(task);
1132
+}
1133
+
1134
+function finishFunctionComponent(
1135
+ request: Request,
1136
+ task: Task,
1137
+ children: ReactNodeList,
1138
+ hasId: boolean,
1139
+ formStateCount: number,
1140
+ formStateMatchingIndex: number,
1141
+) {
1142
+ let didEmitFormStateMarkers = false;
1143
+ if (formStateCount !== 0) {
1144
+ // For each useFormState hook, emit a marker that indicates whether we
1145
+ // rendered using the form state passed at the root.
1146
+ // TODO: As an optimization, Fizz should only emit these markers if form
1147
+ // state is passed at the root.
1148
+ const segment = task.blockedSegment;
1149
+ if (segment === null) {
1150
+ // Implies we're in reumable mode.
1151
} else {
1132
- renderNodeDestructive(request, task, null, value, 0);
1152
+ didEmitFormStateMarkers = true;
1153
+ const target = segment.chunks;
1154
+ for (let i = 0; i < formStateCount; i++) {
1155
+ if (i === formStateMatchingIndex) {
1156
+ pushFormStateMarkerIsMatching(target);
1157
+ } else {
1158
+ pushFormStateMarkerIsNotMatching(target);
1159
+ }
1160
+ }
1161
}
1162
}
1135
- popComponentStackInDEV(task);
1163
+
1164
+ if (hasId) {
1165
+ // This component materialized an id. We treat this as its own level, with
1166
+ // a single "child" slot.
1167
+ const prevTreeContext = task.treeContext;
1168
+ const totalChildren = 1;
1169
+ const index = 0;
1170
+ // Modify the id context. Because we'll need to reset this if something
1171
+ // suspends or errors, we'll use the non-destructive render path.
1172
+ task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
1173
+ renderNode(request, task, children, 0);
1174
+ // Like the other contexts, this does not need to be in a finally block
1175
+ // because renderNode takes care of unwinding the stack.
1176
+ task.treeContext = prevTreeContext;
1177
+ } else if (didEmitFormStateMarkers) {
1178
+ // If there were formState hooks, we must use the non-destructive path
1179
+ // because this component is not a pure indirection; we emitted markers
1180
+ // to the stream.
1181
+ renderNode(request, task, children, 0);
1182
+ } else {
1183
+ // We're now successfully past this task, and we haven't modified the
1184
+ // context stack. We don't have to pop back to the previous task every
1185
+ // again, so we can use the destructive recursive form.
1186
+ renderNodeDestructive(request, task, null, children, 0);
1187
+ }
1188
}
1189
1190
function validateFunctionComponentInDev(Component: any): void {
1273
ref,
1274
);
1275
const hasId = checkDidRenderIdHook();
1224
- if (hasId) {
1225
- // This component materialized an id. We treat this as its own level, with
1226
- // a single "child" slot.
1227
- const prevTreeContext = task.treeContext;
1228
- const totalChildren = 1;
1229
- const index = 0;
1230
- // Modify the id context. Because we'll need to reset this if something
1231
- // suspends or errors, we'll use the non-destructive render path.
1232
- task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
1233
- renderNode(request, task, children, 0);
1234
- // Like the other contexts, this does not need to be in a finally block
1235
- // because renderNode takes care of unwinding the stack.
1236
- } else {
1237
- renderNodeDestructive(request, task, null, children, 0);
1238
- }
1276
+ const formStateCount = getFormStateCount();
1277
+ const formStateMatchingIndex = getFormStateMatchingIndex();
1278
+ finishFunctionComponent(
1279
+ request,
1280
+ task,
1281
+ children,
1282
+ hasId,
1283
+ formStateCount,
1284
+ formStateMatchingIndex,
1285
+ );
1286
popComponentStackInDEV(task);
1287
}
1288