Best JavaScript code snippet using playwright-internal
ReactChildFiber.new.js
Source:ReactChildFiber.new.js
...218 : newChild,219 );220 }221}222function warnOnFunctionType(returnFiber ) {223 if (__DEV__) {224 const componentName = getComponentName(returnFiber.type) || 'Component';225 if (ownerHasFunctionTypeWarning[componentName]) {226 return;227 }228 ownerHasFunctionTypeWarning[componentName] = true;229 console.error(230 'Functions are not valid as a React child. This may happen if ' +231 'you return a Component instead of <Component /> from render. ' +232 'Or maybe you meant to call this function rather than return it.',233 );234 }235}236// We avoid inlining this to avoid potential deopts from using try/catch.237/** @noinline */238function resolveLazyType (239 lazyComponent ,240) {241 try {242 // If we can, let's peek at the resulting type.243 const payload = lazyComponent._payload;244 const init = lazyComponent._init;245 return init(payload);246 } catch (x) {247 // Leave it in place and let it throw again in the begin phase.248 return lazyComponent;249 }250}251// This wrapper function exists because I expect to clone the code in each path252// to be able to optimize each path individually by branching early. This needs253// a compiler or we can do it manually. Helpers that don't need this branching254// live outside of this function.255function ChildReconciler(shouldTrackSideEffects) {256 function deleteChild(returnFiber , childToDelete ) {257 if (!shouldTrackSideEffects) {258 // Noop.259 return;260 }261 const deletions = returnFiber.deletions;262 if (deletions === null) {263 returnFiber.deletions = [childToDelete];264 returnFiber.flags |= Deletion;265 } else {266 deletions.push(childToDelete);267 }268 }269 function deleteRemainingChildren(270 returnFiber ,271 currentFirstChild ,272 ) {273 if (!shouldTrackSideEffects) {274 // Noop.275 return null;276 }277 // TODO: For the shouldClone case, this could be micro-optimized a bit by278 // assuming that after the first child we've already added everything.279 let childToDelete = currentFirstChild;280 while (childToDelete !== null) {281 deleteChild(returnFiber, childToDelete);282 childToDelete = childToDelete.sibling;283 }284 return null;285 }286 function mapRemainingChildren(287 returnFiber ,288 currentFirstChild ,289 ) {290 // Add the remaining children to a temporary map so that we can find them by291 // keys quickly. Implicit (null) keys get added to this set with their index292 // instead.293 const existingChildren = new Map();294 let existingChild = currentFirstChild;295 while (existingChild !== null) {296 if (existingChild.key !== null) {297 existingChildren.set(existingChild.key, existingChild);298 } else {299 existingChildren.set(existingChild.index, existingChild);300 }301 existingChild = existingChild.sibling;302 }303 return existingChildren;304 }305 function useFiber(fiber , pendingProps ) {306 // We currently set sibling to null and index to 0 here because it is easy307 // to forget to do before returning it. E.g. for the single child case.308 const clone = createWorkInProgress(fiber, pendingProps);309 clone.index = 0;310 clone.sibling = null;311 return clone;312 }313 function placeChild(314 newFiber ,315 lastPlacedIndex ,316 newIndex ,317 ) {318 newFiber.index = newIndex;319 if (!shouldTrackSideEffects) {320 // Noop.321 return lastPlacedIndex;322 }323 const current = newFiber.alternate;324 if (current !== null) {325 const oldIndex = current.index;326 if (oldIndex < lastPlacedIndex) {327 // This is a move.328 newFiber.flags = Placement;329 return lastPlacedIndex;330 } else {331 // This item can stay in place.332 return oldIndex;333 }334 } else {335 // This is an insertion.336 newFiber.flags = Placement;337 return lastPlacedIndex;338 }339 }340 function placeSingleChild(newFiber ) {341 // This is simpler for the single child case. We only need to do a342 // placement for inserting new children.343 if (shouldTrackSideEffects && newFiber.alternate === null) {344 newFiber.flags = Placement;345 }346 return newFiber;347 }348 function updateTextNode(349 returnFiber ,350 current ,351 textContent ,352 lanes ,353 ) {354 if (current === null || current.tag !== HostText) {355 // Insert356 const created = createFiberFromText(textContent, returnFiber.mode, lanes);357 created.return = returnFiber;358 return created;359 } else {360 // Update361 const existing = useFiber(current, textContent);362 existing.return = returnFiber;363 return existing;364 }365 }366 function updateElement(367 returnFiber ,368 current ,369 element ,370 lanes ,371 ) {372 if (current !== null) {373 if (374 current.elementType === element.type ||375 // Keep this check inline so it only runs on the false path:376 (__DEV__ ? isCompatibleFamilyForHotReloading(current, element) : false)377 ) {378 // Move based on index379 const existing = useFiber(current, element.props);380 existing.ref = coerceRef(returnFiber, current, element);381 existing.return = returnFiber;382 if (__DEV__) {383 existing._debugSource = element._source;384 existing._debugOwner = element._owner;385 }386 return existing;387 } else if (enableBlocksAPI && current.tag === Block) {388 // The new Block might not be initialized yet. We need to initialize389 // it in case initializing it turns out it would match.390 let type = element.type;391 if (type.$$typeof === REACT_LAZY_TYPE) {392 type = resolveLazyType(type);393 }394 if (395 type.$$typeof === REACT_BLOCK_TYPE &&396 ((type ) )._render ===397 (current.type )._render398 ) {399 // Same as above but also update the .type field.400 const existing = useFiber(current, element.props);401 existing.return = returnFiber;402 existing.type = type;403 if (__DEV__) {404 existing._debugSource = element._source;405 existing._debugOwner = element._owner;406 }407 return existing;408 }409 }410 }411 // Insert412 const created = createFiberFromElement(element, returnFiber.mode, lanes);413 created.ref = coerceRef(returnFiber, current, element);414 created.return = returnFiber;415 return created;416 }417 function updatePortal(418 returnFiber ,419 current ,420 portal ,421 lanes ,422 ) {423 if (424 current === null ||425 current.tag !== HostPortal ||426 current.stateNode.containerInfo !== portal.containerInfo ||427 current.stateNode.implementation !== portal.implementation428 ) {429 // Insert430 const created = createFiberFromPortal(portal, returnFiber.mode, lanes);431 created.return = returnFiber;432 return created;433 } else {434 // Update435 const existing = useFiber(current, portal.children || []);436 existing.return = returnFiber;437 return existing;438 }439 }440 function updateFragment(441 returnFiber ,442 current ,443 fragment ,444 lanes ,445 key ,446 ) {447 if (current === null || current.tag !== Fragment) {448 // Insert449 const created = createFiberFromFragment(450 fragment,451 returnFiber.mode,452 lanes,453 key,454 );455 created.return = returnFiber;456 return created;457 } else {458 // Update459 const existing = useFiber(current, fragment);460 existing.return = returnFiber;461 return existing;462 }463 }464 function createChild(465 returnFiber ,466 newChild ,467 lanes ,468 ) {469 if (typeof newChild === 'string' || typeof newChild === 'number') {470 // Text nodes don't have keys. If the previous node is implicitly keyed471 // we can continue to replace it without aborting even if it is not a text472 // node.473 const created = createFiberFromText(474 '' + newChild,475 returnFiber.mode,476 lanes,477 );478 created.return = returnFiber;479 return created;480 }481 if (typeof newChild === 'object' && newChild !== null) {482 switch (newChild.$$typeof) {483 case REACT_ELEMENT_TYPE: {484 const created = createFiberFromElement(485 newChild,486 returnFiber.mode,487 lanes,488 );489 created.ref = coerceRef(returnFiber, null, newChild);490 created.return = returnFiber;491 return created;492 }493 case REACT_PORTAL_TYPE: {494 const created = createFiberFromPortal(495 newChild,496 returnFiber.mode,497 lanes,498 );499 created.return = returnFiber;500 return created;501 }502 case REACT_LAZY_TYPE: {503 if (enableLazyElements) {504 const payload = newChild._payload;505 const init = newChild._init;506 return createChild(returnFiber, init(payload), lanes);507 }508 }509 }510 if (isArray(newChild) || getIteratorFn(newChild)) {511 const created = createFiberFromFragment(512 newChild,513 returnFiber.mode,514 lanes,515 null,516 );517 created.return = returnFiber;518 return created;519 }520 throwOnInvalidObjectType(returnFiber, newChild);521 }522 if (__DEV__) {523 if (typeof newChild === 'function') {524 warnOnFunctionType(returnFiber);525 }526 }527 return null;528 }529 function updateSlot(530 returnFiber ,531 oldFiber ,532 newChild ,533 lanes ,534 ) {535 // Update the fiber if the keys match, otherwise return null.536 const key = oldFiber !== null ? oldFiber.key : null;537 if (typeof newChild === 'string' || typeof newChild === 'number') {538 // Text nodes don't have keys. If the previous node is implicitly keyed539 // we can continue to replace it without aborting even if it is not a text540 // node.541 if (key !== null) {542 return null;543 }544 return updateTextNode(returnFiber, oldFiber, '' + newChild, lanes);545 }546 if (typeof newChild === 'object' && newChild !== null) {547 switch (newChild.$$typeof) {548 case REACT_ELEMENT_TYPE: {549 if (newChild.key === key) {550 if (newChild.type === REACT_FRAGMENT_TYPE) {551 return updateFragment(552 returnFiber,553 oldFiber,554 newChild.props.children,555 lanes,556 key,557 );558 }559 return updateElement(returnFiber, oldFiber, newChild, lanes);560 } else {561 return null;562 }563 }564 case REACT_PORTAL_TYPE: {565 if (newChild.key === key) {566 return updatePortal(returnFiber, oldFiber, newChild, lanes);567 } else {568 return null;569 }570 }571 case REACT_LAZY_TYPE: {572 if (enableLazyElements) {573 const payload = newChild._payload;574 const init = newChild._init;575 return updateSlot(returnFiber, oldFiber, init(payload), lanes);576 }577 }578 }579 if (isArray(newChild) || getIteratorFn(newChild)) {580 if (key !== null) {581 return null;582 }583 return updateFragment(returnFiber, oldFiber, newChild, lanes, null);584 }585 throwOnInvalidObjectType(returnFiber, newChild);586 }587 if (__DEV__) {588 if (typeof newChild === 'function') {589 warnOnFunctionType(returnFiber);590 }591 }592 return null;593 }594 function updateFromMap(595 existingChildren ,596 returnFiber ,597 newIdx ,598 newChild ,599 lanes ,600 ) {601 if (typeof newChild === 'string' || typeof newChild === 'number') {602 // Text nodes don't have keys, so we neither have to check the old nor603 // new node for the key. If both are text nodes, they match.604 const matchedFiber = existingChildren.get(newIdx) || null;605 return updateTextNode(returnFiber, matchedFiber, '' + newChild, lanes);606 }607 if (typeof newChild === 'object' && newChild !== null) {608 switch (newChild.$$typeof) {609 case REACT_ELEMENT_TYPE: {610 const matchedFiber =611 existingChildren.get(612 newChild.key === null ? newIdx : newChild.key,613 ) || null;614 if (newChild.type === REACT_FRAGMENT_TYPE) {615 return updateFragment(616 returnFiber,617 matchedFiber,618 newChild.props.children,619 lanes,620 newChild.key,621 );622 }623 return updateElement(returnFiber, matchedFiber, newChild, lanes);624 }625 case REACT_PORTAL_TYPE: {626 const matchedFiber =627 existingChildren.get(628 newChild.key === null ? newIdx : newChild.key,629 ) || null;630 return updatePortal(returnFiber, matchedFiber, newChild, lanes);631 }632 case REACT_LAZY_TYPE:633 if (enableLazyElements) {634 const payload = newChild._payload;635 const init = newChild._init;636 return updateFromMap(637 existingChildren,638 returnFiber,639 newIdx,640 init(payload),641 lanes,642 );643 }644 }645 if (isArray(newChild) || getIteratorFn(newChild)) {646 const matchedFiber = existingChildren.get(newIdx) || null;647 return updateFragment(returnFiber, matchedFiber, newChild, lanes, null);648 }649 throwOnInvalidObjectType(returnFiber, newChild);650 }651 if (__DEV__) {652 if (typeof newChild === 'function') {653 warnOnFunctionType(returnFiber);654 }655 }656 return null;657 }658 /**659 * Warns if there is a duplicate or missing key660 */661 function warnOnInvalidKey(662 child ,663 knownKeys ,664 returnFiber ,665 ) {666 if (__DEV__) {667 if (typeof child !== 'object' || child === null) {668 return knownKeys;669 }670 switch (child.$$typeof) {671 case REACT_ELEMENT_TYPE:672 case REACT_PORTAL_TYPE:673 warnForMissingKey(child, returnFiber);674 const key = child.key;675 if (typeof key !== 'string') {676 break;677 }678 if (knownKeys === null) {679 knownKeys = new Set();680 knownKeys.add(key);681 break;682 }683 if (!knownKeys.has(key)) {684 knownKeys.add(key);685 break;686 }687 console.error(688 'Encountered two children with the same key, `%s`. ' +689 'Keys should be unique so that components maintain their identity ' +690 'across updates. Non-unique keys may cause children to be ' +691 'duplicated and/or omitted â the behavior is unsupported and ' +692 'could change in a future version.',693 key,694 );695 break;696 case REACT_LAZY_TYPE:697 if (enableLazyElements) {698 const payload = child._payload;699 const init = (child._init );700 warnOnInvalidKey(init(payload), knownKeys, returnFiber);701 break;702 }703 // We intentionally fallthrough here if enableLazyElements is not on.704 // eslint-disable-next-lined no-fallthrough705 default:706 break;707 }708 }709 return knownKeys;710 }711 function reconcileChildrenArray(712 returnFiber ,713 currentFirstChild ,714 newChildren ,715 lanes ,716 ) {717 // This algorithm can't optimize by searching from both ends since we718 // don't have backpointers on fibers. I'm trying to see how far we can get719 // with that model. If it ends up not being worth the tradeoffs, we can720 // add it later.721 // Even with a two ended optimization, we'd want to optimize for the case722 // where there are few changes and brute force the comparison instead of723 // going for the Map. It'd like to explore hitting that path first in724 // forward-only mode and only go for the Map once we notice that we need725 // lots of look ahead. This doesn't handle reversal as well as two ended726 // search but that's unusual. Besides, for the two ended optimization to727 // work on Iterables, we'd need to copy the whole set.728 // In this first iteration, we'll just live with hitting the bad case729 // (adding everything to a Map) in for every insert/move.730 // If you change this code, also update reconcileChildrenIterator() which731 // uses the same algorithm.732 if (__DEV__) {733 // First, validate keys.734 let knownKeys = null;735 for (let i = 0; i < newChildren.length; i++) {736 const child = newChildren[i];737 knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);738 }739 }740 let resultingFirstChild = null;741 let previousNewFiber = null;742 let oldFiber = currentFirstChild;743 let lastPlacedIndex = 0;744 let newIdx = 0;745 let nextOldFiber = null;746 for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {747 if (oldFiber.index > newIdx) {748 nextOldFiber = oldFiber;749 oldFiber = null;750 } else {751 nextOldFiber = oldFiber.sibling;752 }753 const newFiber = updateSlot(754 returnFiber,755 oldFiber,756 newChildren[newIdx],757 lanes,758 );759 if (newFiber === null) {760 // TODO: This breaks on empty slots like null children. That's761 // unfortunate because it triggers the slow path all the time. We need762 // a better way to communicate whether this was a miss or null,763 // boolean, undefined, etc.764 if (oldFiber === null) {765 oldFiber = nextOldFiber;766 }767 break;768 }769 if (shouldTrackSideEffects) {770 if (oldFiber && newFiber.alternate === null) {771 // We matched the slot, but we didn't reuse the existing fiber, so we772 // need to delete the existing child.773 deleteChild(returnFiber, oldFiber);774 }775 }776 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);777 if (previousNewFiber === null) {778 // TODO: Move out of the loop. This only happens for the first run.779 resultingFirstChild = newFiber;780 } else {781 // TODO: Defer siblings if we're not at the right index for this slot.782 // I.e. if we had null values before, then we want to defer this783 // for each null value. However, we also don't want to call updateSlot784 // with the previous one.785 previousNewFiber.sibling = newFiber;786 }787 previousNewFiber = newFiber;788 oldFiber = nextOldFiber;789 }790 if (newIdx === newChildren.length) {791 // We've reached the end of the new children. We can delete the rest.792 deleteRemainingChildren(returnFiber, oldFiber);793 return resultingFirstChild;794 }795 if (oldFiber === null) {796 // If we don't have any more existing children we can choose a fast path797 // since the rest will all be insertions.798 for (; newIdx < newChildren.length; newIdx++) {799 const newFiber = createChild(returnFiber, newChildren[newIdx], lanes);800 if (newFiber === null) {801 continue;802 }803 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);804 if (previousNewFiber === null) {805 // TODO: Move out of the loop. This only happens for the first run.806 resultingFirstChild = newFiber;807 } else {808 previousNewFiber.sibling = newFiber;809 }810 previousNewFiber = newFiber;811 }812 return resultingFirstChild;813 }814 // Add all children to a key map for quick lookups.815 const existingChildren = mapRemainingChildren(returnFiber, oldFiber);816 // Keep scanning and use the map to restore deleted items as moves.817 for (; newIdx < newChildren.length; newIdx++) {818 const newFiber = updateFromMap(819 existingChildren,820 returnFiber,821 newIdx,822 newChildren[newIdx],823 lanes,824 );825 if (newFiber !== null) {826 if (shouldTrackSideEffects) {827 if (newFiber.alternate !== null) {828 // The new fiber is a work in progress, but if there exists a829 // current, that means that we reused the fiber. We need to delete830 // it from the child list so that we don't add it to the deletion831 // list.832 existingChildren.delete(833 newFiber.key === null ? newIdx : newFiber.key,834 );835 }836 }837 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);838 if (previousNewFiber === null) {839 resultingFirstChild = newFiber;840 } else {841 previousNewFiber.sibling = newFiber;842 }843 previousNewFiber = newFiber;844 }845 }846 if (shouldTrackSideEffects) {847 // Any existing children that weren't consumed above were deleted. We need848 // to add them to the deletion list.849 existingChildren.forEach(child => deleteChild(returnFiber, child));850 }851 return resultingFirstChild;852 }853 function reconcileChildrenIterator(854 returnFiber ,855 currentFirstChild ,856 newChildrenIterable ,857 lanes ,858 ) {859 // This is the same implementation as reconcileChildrenArray(),860 // but using the iterator instead.861 const iteratorFn = getIteratorFn(newChildrenIterable);862 invariant(863 typeof iteratorFn === 'function',864 'An object is not an iterable. This error is likely caused by a bug in ' +865 'React. Please file an issue.',866 );867 if (__DEV__) {868 // We don't support rendering Generators because it's a mutation.869 // See https://github.com/facebook/react/issues/12995870 if (871 typeof Symbol === 'function' &&872 // $FlowFixMe Flow doesn't know about toStringTag873 newChildrenIterable[Symbol.toStringTag] === 'Generator'874 ) {875 if (!didWarnAboutGenerators) {876 console.error(877 'Using Generators as children is unsupported and will likely yield ' +878 'unexpected results because enumerating a generator mutates it. ' +879 'You may convert it to an array with `Array.from()` or the ' +880 '`[...spread]` operator before rendering. Keep in mind ' +881 'you might need to polyfill these features for older browsers.',882 );883 }884 didWarnAboutGenerators = true;885 }886 // Warn about using Maps as children887 if ((newChildrenIterable ).entries === iteratorFn) {888 if (!didWarnAboutMaps) {889 console.error(890 'Using Maps as children is not supported. ' +891 'Use an array of keyed ReactElements instead.',892 );893 }894 didWarnAboutMaps = true;895 }896 // First, validate keys.897 // We'll get a different iterator later for the main pass.898 const newChildren = iteratorFn.call(newChildrenIterable);899 if (newChildren) {900 let knownKeys = null;901 let step = newChildren.next();902 for (; !step.done; step = newChildren.next()) {903 const child = step.value;904 knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);905 }906 }907 }908 const newChildren = iteratorFn.call(newChildrenIterable);909 invariant(newChildren != null, 'An iterable object provided no iterator.');910 let resultingFirstChild = null;911 let previousNewFiber = null;912 let oldFiber = currentFirstChild;913 let lastPlacedIndex = 0;914 let newIdx = 0;915 let nextOldFiber = null;916 let step = newChildren.next();917 for (918 ;919 oldFiber !== null && !step.done;920 newIdx++, step = newChildren.next()921 ) {922 if (oldFiber.index > newIdx) {923 nextOldFiber = oldFiber;924 oldFiber = null;925 } else {926 nextOldFiber = oldFiber.sibling;927 }928 const newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);929 if (newFiber === null) {930 // TODO: This breaks on empty slots like null children. That's931 // unfortunate because it triggers the slow path all the time. We need932 // a better way to communicate whether this was a miss or null,933 // boolean, undefined, etc.934 if (oldFiber === null) {935 oldFiber = nextOldFiber;936 }937 break;938 }939 if (shouldTrackSideEffects) {940 if (oldFiber && newFiber.alternate === null) {941 // We matched the slot, but we didn't reuse the existing fiber, so we942 // need to delete the existing child.943 deleteChild(returnFiber, oldFiber);944 }945 }946 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);947 if (previousNewFiber === null) {948 // TODO: Move out of the loop. This only happens for the first run.949 resultingFirstChild = newFiber;950 } else {951 // TODO: Defer siblings if we're not at the right index for this slot.952 // I.e. if we had null values before, then we want to defer this953 // for each null value. However, we also don't want to call updateSlot954 // with the previous one.955 previousNewFiber.sibling = newFiber;956 }957 previousNewFiber = newFiber;958 oldFiber = nextOldFiber;959 }960 if (step.done) {961 // We've reached the end of the new children. We can delete the rest.962 deleteRemainingChildren(returnFiber, oldFiber);963 return resultingFirstChild;964 }965 if (oldFiber === null) {966 // If we don't have any more existing children we can choose a fast path967 // since the rest will all be insertions.968 for (; !step.done; newIdx++, step = newChildren.next()) {969 const newFiber = createChild(returnFiber, step.value, lanes);970 if (newFiber === null) {971 continue;972 }973 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);974 if (previousNewFiber === null) {975 // TODO: Move out of the loop. This only happens for the first run.976 resultingFirstChild = newFiber;977 } else {978 previousNewFiber.sibling = newFiber;979 }980 previousNewFiber = newFiber;981 }982 return resultingFirstChild;983 }984 // Add all children to a key map for quick lookups.985 const existingChildren = mapRemainingChildren(returnFiber, oldFiber);986 // Keep scanning and use the map to restore deleted items as moves.987 for (; !step.done; newIdx++, step = newChildren.next()) {988 const newFiber = updateFromMap(989 existingChildren,990 returnFiber,991 newIdx,992 step.value,993 lanes,994 );995 if (newFiber !== null) {996 if (shouldTrackSideEffects) {997 if (newFiber.alternate !== null) {998 // The new fiber is a work in progress, but if there exists a999 // current, that means that we reused the fiber. We need to delete1000 // it from the child list so that we don't add it to the deletion1001 // list.1002 existingChildren.delete(1003 newFiber.key === null ? newIdx : newFiber.key,1004 );1005 }1006 }1007 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);1008 if (previousNewFiber === null) {1009 resultingFirstChild = newFiber;1010 } else {1011 previousNewFiber.sibling = newFiber;1012 }1013 previousNewFiber = newFiber;1014 }1015 }1016 if (shouldTrackSideEffects) {1017 // Any existing children that weren't consumed above were deleted. We need1018 // to add them to the deletion list.1019 existingChildren.forEach(child => deleteChild(returnFiber, child));1020 }1021 return resultingFirstChild;1022 }1023 function reconcileSingleTextNode(1024 returnFiber ,1025 currentFirstChild ,1026 textContent ,1027 lanes ,1028 ) {1029 // There's no need to check for keys on text nodes since we don't have a1030 // way to define them.1031 if (currentFirstChild !== null && currentFirstChild.tag === HostText) {1032 // We already have an existing node so let's just update it and delete1033 // the rest.1034 deleteRemainingChildren(returnFiber, currentFirstChild.sibling);1035 const existing = useFiber(currentFirstChild, textContent);1036 existing.return = returnFiber;1037 return existing;1038 }1039 // The existing first child is not a text node so we need to create one1040 // and delete the existing ones.1041 deleteRemainingChildren(returnFiber, currentFirstChild);1042 const created = createFiberFromText(textContent, returnFiber.mode, lanes);1043 created.return = returnFiber;1044 return created;1045 }1046 function reconcileSingleElement(1047 returnFiber ,1048 currentFirstChild ,1049 element ,1050 lanes ,1051 ) {1052 const key = element.key;1053 let child = currentFirstChild;1054 while (child !== null) {1055 // TODO: If key === null and child.key === null, then this only applies to1056 // the first item in the list.1057 if (child.key === key) {1058 switch (child.tag) {1059 case Fragment: {1060 if (element.type === REACT_FRAGMENT_TYPE) {1061 deleteRemainingChildren(returnFiber, child.sibling);1062 const existing = useFiber(child, element.props.children);1063 existing.return = returnFiber;1064 if (__DEV__) {1065 existing._debugSource = element._source;1066 existing._debugOwner = element._owner;1067 }1068 return existing;1069 }1070 break;1071 }1072 case Block:1073 if (enableBlocksAPI) {1074 let type = element.type;1075 if (type.$$typeof === REACT_LAZY_TYPE) {1076 type = resolveLazyType(type);1077 }1078 if (type.$$typeof === REACT_BLOCK_TYPE) {1079 // The new Block might not be initialized yet. We need to initialize1080 // it in case initializing it turns out it would match.1081 if (1082 ((type ) )._render ===1083 (child.type )._render1084 ) {1085 deleteRemainingChildren(returnFiber, child.sibling);1086 const existing = useFiber(child, element.props);1087 existing.type = type;1088 existing.return = returnFiber;1089 if (__DEV__) {1090 existing._debugSource = element._source;1091 existing._debugOwner = element._owner;1092 }1093 return existing;1094 }1095 }1096 }1097 // We intentionally fallthrough here if enableBlocksAPI is not on.1098 // eslint-disable-next-lined no-fallthrough1099 default: {1100 if (1101 child.elementType === element.type ||1102 // Keep this check inline so it only runs on the false path:1103 (__DEV__1104 ? isCompatibleFamilyForHotReloading(child, element)1105 : false)1106 ) {1107 deleteRemainingChildren(returnFiber, child.sibling);1108 const existing = useFiber(child, element.props);1109 existing.ref = coerceRef(returnFiber, child, element);1110 existing.return = returnFiber;1111 if (__DEV__) {1112 existing._debugSource = element._source;1113 existing._debugOwner = element._owner;1114 }1115 return existing;1116 }1117 break;1118 }1119 }1120 // Didn't match.1121 deleteRemainingChildren(returnFiber, child);1122 break;1123 } else {1124 deleteChild(returnFiber, child);1125 }1126 child = child.sibling;1127 }1128 if (element.type === REACT_FRAGMENT_TYPE) {1129 const created = createFiberFromFragment(1130 element.props.children,1131 returnFiber.mode,1132 lanes,1133 element.key,1134 );1135 created.return = returnFiber;1136 return created;1137 } else {1138 const created = createFiberFromElement(element, returnFiber.mode, lanes);1139 created.ref = coerceRef(returnFiber, currentFirstChild, element);1140 created.return = returnFiber;1141 return created;1142 }1143 }1144 function reconcileSinglePortal(1145 returnFiber ,1146 currentFirstChild ,1147 portal ,1148 lanes ,1149 ) {1150 const key = portal.key;1151 let child = currentFirstChild;1152 while (child !== null) {1153 // TODO: If key === null and child.key === null, then this only applies to1154 // the first item in the list.1155 if (child.key === key) {1156 if (1157 child.tag === HostPortal &&1158 child.stateNode.containerInfo === portal.containerInfo &&1159 child.stateNode.implementation === portal.implementation1160 ) {1161 deleteRemainingChildren(returnFiber, child.sibling);1162 const existing = useFiber(child, portal.children || []);1163 existing.return = returnFiber;1164 return existing;1165 } else {1166 deleteRemainingChildren(returnFiber, child);1167 break;1168 }1169 } else {1170 deleteChild(returnFiber, child);1171 }1172 child = child.sibling;1173 }1174 const created = createFiberFromPortal(portal, returnFiber.mode, lanes);1175 created.return = returnFiber;1176 return created;1177 }1178 // This API will tag the children with the side-effect of the reconciliation1179 // itself. They will be added to the side-effect list as we pass through the1180 // children and the parent.1181 function reconcileChildFibers(1182 returnFiber ,1183 currentFirstChild ,1184 newChild ,1185 lanes ,1186 ) {1187 // This function is not recursive.1188 // If the top level item is an array, we treat it as a set of children,1189 // not as a fragment. Nested arrays on the other hand will be treated as1190 // fragment nodes. Recursion happens at the normal flow.1191 // Handle top level unkeyed fragments as if they were arrays.1192 // This leads to an ambiguity between <>{[...]}</> and <>...</>.1193 // We treat the ambiguous cases above the same.1194 const isUnkeyedTopLevelFragment =1195 typeof newChild === 'object' &&1196 newChild !== null &&1197 newChild.type === REACT_FRAGMENT_TYPE &&1198 newChild.key === null;1199 if (isUnkeyedTopLevelFragment) {1200 newChild = newChild.props.children;1201 }1202 // Handle object types1203 const isObject = typeof newChild === 'object' && newChild !== null;1204 if (isObject) {1205 switch (newChild.$$typeof) {1206 case REACT_ELEMENT_TYPE:1207 return placeSingleChild(1208 reconcileSingleElement(1209 returnFiber,1210 currentFirstChild,1211 newChild,1212 lanes,1213 ),1214 );1215 case REACT_PORTAL_TYPE:1216 return placeSingleChild(1217 reconcileSinglePortal(1218 returnFiber,1219 currentFirstChild,1220 newChild,1221 lanes,1222 ),1223 );1224 case REACT_LAZY_TYPE:1225 if (enableLazyElements) {1226 const payload = newChild._payload;1227 const init = newChild._init;1228 // TODO: This function is supposed to be non-recursive.1229 return reconcileChildFibers(1230 returnFiber,1231 currentFirstChild,1232 init(payload),1233 lanes,1234 );1235 }1236 }1237 }1238 if (typeof newChild === 'string' || typeof newChild === 'number') {1239 return placeSingleChild(1240 reconcileSingleTextNode(1241 returnFiber,1242 currentFirstChild,1243 '' + newChild,1244 lanes,1245 ),1246 );1247 }1248 if (isArray(newChild)) {1249 return reconcileChildrenArray(1250 returnFiber,1251 currentFirstChild,1252 newChild,1253 lanes,1254 );1255 }1256 if (getIteratorFn(newChild)) {1257 return reconcileChildrenIterator(1258 returnFiber,1259 currentFirstChild,1260 newChild,1261 lanes,1262 );1263 }1264 if (isObject) {1265 throwOnInvalidObjectType(returnFiber, newChild);1266 }1267 if (__DEV__) {1268 if (typeof newChild === 'function') {1269 warnOnFunctionType(returnFiber);1270 }1271 }1272 if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {1273 // If the new child is undefined, and the return fiber is a composite1274 // component, throw an error. If Fiber return types are disabled,1275 // we already threw above.1276 switch (returnFiber.tag) {1277 case ClassComponent: {1278 if (__DEV__) {1279 const instance = returnFiber.stateNode;1280 if (instance.render._isMockFunction) {1281 // We allow auto-mocks to proceed as if they're returning null.1282 break;1283 }...
ReactChildFiber.old.js
Source:ReactChildFiber.old.js
...114 }115 }116 }117 }118 function warnOnFunctionType(returnFiber) {119 {120 var componentName = getComponentName(returnFiber.type) || 'Component';121 if (ownerHasFunctionTypeWarning[componentName]) {122 return;123 }124 ownerHasFunctionTypeWarning[componentName] = true;125 error('Functions are not valid as a React child. This may happen if ' + 'you return a Component instead of <Component /> from render. ' + 'Or maybe you meant to call this function rather than return it.');126 }127 } // We avoid inlining this to avoid potential deopts from using try/catch.128 /** @noinline */129 function resolveLazyType(lazyComponent) {130 try {131 // If we can, let's peek at the resulting type.132 var payload = lazyComponent._payload;133 var init = lazyComponent._init;134 return init(payload);135 } catch (x) {136 // Leave it in place and let it throw again in the begin phase.137 return lazyComponent;138 }139 } // This wrapper function exists because I expect to clone the code in each path140 // to be able to optimize each path individually by branching early. This needs141 // a compiler or we can do it manually. Helpers that don't need this branching142 // live outside of this function.143 function ChildReconciler(shouldTrackSideEffects) {144 function deleteChild(returnFiber, childToDelete) {145 if (!shouldTrackSideEffects) {146 // Noop.147 return;148 } // Deletions are added in reversed order so we add it to the front.149 // At this point, the return fiber's effect list is empty except for150 // deletions, so we can just append the deletion to the list. The remaining151 // effects aren't added until the complete phase. Once we implement152 // resuming, this may not be true.153 var last = returnFiber.lastEffect;154 if (last !== null) {155 last.nextEffect = childToDelete;156 returnFiber.lastEffect = childToDelete;157 } else {158 returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;159 }160 childToDelete.nextEffect = null;161 childToDelete.flags = Deletion;162 }163 function deleteRemainingChildren(returnFiber, currentFirstChild) {164 if (!shouldTrackSideEffects) {165 // Noop.166 return null;167 } // TODO: For the shouldClone case, this could be micro-optimized a bit by168 // assuming that after the first child we've already added everything.169 var childToDelete = currentFirstChild;170 while (childToDelete !== null) {171 deleteChild(returnFiber, childToDelete);172 childToDelete = childToDelete.sibling;173 }174 return null;175 }176 function mapRemainingChildren(returnFiber, currentFirstChild) {177 // Add the remaining children to a temporary map so that we can find them by178 // keys quickly. Implicit (null) keys get added to this set with their index179 // instead.180 var existingChildren = new Map();181 var existingChild = currentFirstChild;182 while (existingChild !== null) {183 if (existingChild.key !== null) {184 existingChildren.set(existingChild.key, existingChild);185 } else {186 existingChildren.set(existingChild.index, existingChild);187 }188 existingChild = existingChild.sibling;189 }190 return existingChildren;191 }192 function useFiber(fiber, pendingProps) {193 // We currently set sibling to null and index to 0 here because it is easy194 // to forget to do before returning it. E.g. for the single child case.195 var clone = createWorkInProgress(fiber, pendingProps);196 clone.index = 0;197 clone.sibling = null;198 return clone;199 }200 function placeChild(newFiber, lastPlacedIndex, newIndex) {201 newFiber.index = newIndex;202 if (!shouldTrackSideEffects) {203 // Noop.204 return lastPlacedIndex;205 }206 var current = newFiber.alternate;207 if (current !== null) {208 var oldIndex = current.index;209 if (oldIndex < lastPlacedIndex) {210 // This is a move.211 newFiber.flags = Placement;212 return lastPlacedIndex;213 } else {214 // This item can stay in place.215 return oldIndex;216 }217 } else {218 // This is an insertion.219 newFiber.flags = Placement;220 return lastPlacedIndex;221 }222 }223 function placeSingleChild(newFiber) {224 // This is simpler for the single child case. We only need to do a225 // placement for inserting new children.226 if (shouldTrackSideEffects && newFiber.alternate === null) {227 newFiber.flags = Placement;228 }229 return newFiber;230 }231 function updateTextNode(returnFiber, current, textContent, lanes) {232 if (current === null || current.tag !== HostText) {233 // Insert234 var created = createFiberFromText(textContent, returnFiber.mode, lanes);235 created.return = returnFiber;236 return created;237 } else {238 // Update239 var existing = useFiber(current, textContent);240 existing.return = returnFiber;241 return existing;242 }243 }244 function updateElement(returnFiber, current, element, lanes) {245 if (current !== null) {246 if (current.elementType === element.type || ( // Keep this check inline so it only runs on the false path:247 isCompatibleFamilyForHotReloading(current, element) )) {248 // Move based on index249 var existing = useFiber(current, element.props);250 existing.ref = coerceRef(returnFiber, current, element);251 existing.return = returnFiber;252 {253 existing._debugSource = element._source;254 existing._debugOwner = element._owner;255 }256 return existing;257 } else if ( current.tag === Block) {258 // The new Block might not be initialized yet. We need to initialize259 // it in case initializing it turns out it would match.260 var type = element.type;261 if (type.$$typeof === REACT_LAZY_TYPE) {262 type = resolveLazyType(type);263 }264 if (type.$$typeof === REACT_BLOCK_TYPE && type._render === current.type._render) {265 // Same as above but also update the .type field.266 var _existing = useFiber(current, element.props);267 _existing.return = returnFiber;268 _existing.type = type;269 {270 _existing._debugSource = element._source;271 _existing._debugOwner = element._owner;272 }273 return _existing;274 }275 }276 } // Insert277 var created = createFiberFromElement(element, returnFiber.mode, lanes);278 created.ref = coerceRef(returnFiber, current, element);279 created.return = returnFiber;280 return created;281 }282 function updatePortal(returnFiber, current, portal, lanes) {283 if (current === null || current.tag !== HostPortal || current.stateNode.containerInfo !== portal.containerInfo || current.stateNode.implementation !== portal.implementation) {284 // Insert285 var created = createFiberFromPortal(portal, returnFiber.mode, lanes);286 created.return = returnFiber;287 return created;288 } else {289 // Update290 var existing = useFiber(current, portal.children || []);291 existing.return = returnFiber;292 return existing;293 }294 }295 function updateFragment(returnFiber, current, fragment, lanes, key) {296 if (current === null || current.tag !== Fragment) {297 // Insert298 var created = createFiberFromFragment(fragment, returnFiber.mode, lanes, key);299 created.return = returnFiber;300 return created;301 } else {302 // Update303 var existing = useFiber(current, fragment);304 existing.return = returnFiber;305 return existing;306 }307 }308 function createChild(returnFiber, newChild, lanes) {309 if (typeof newChild === 'string' || typeof newChild === 'number') {310 // Text nodes don't have keys. If the previous node is implicitly keyed311 // we can continue to replace it without aborting even if it is not a text312 // node.313 var created = createFiberFromText('' + newChild, returnFiber.mode, lanes);314 created.return = returnFiber;315 return created;316 }317 if (typeof newChild === 'object' && newChild !== null) {318 switch (newChild.$$typeof) {319 case REACT_ELEMENT_TYPE:320 {321 var _created = createFiberFromElement(newChild, returnFiber.mode, lanes);322 _created.ref = coerceRef(returnFiber, null, newChild);323 _created.return = returnFiber;324 return _created;325 }326 case REACT_PORTAL_TYPE:327 {328 var _created2 = createFiberFromPortal(newChild, returnFiber.mode, lanes);329 _created2.return = returnFiber;330 return _created2;331 }332 case REACT_LAZY_TYPE:333 {334 {335 var payload = newChild._payload;336 var init = newChild._init;337 return createChild(returnFiber, init(payload), lanes);338 }339 }340 }341 if (isArray$1(newChild) || getIteratorFn(newChild)) {342 var _created3 = createFiberFromFragment(newChild, returnFiber.mode, lanes, null);343 _created3.return = returnFiber;344 return _created3;345 }346 throwOnInvalidObjectType(returnFiber, newChild);347 }348 {349 if (typeof newChild === 'function') {350 warnOnFunctionType(returnFiber);351 }352 }353 return null;354 }355 function updateSlot(returnFiber, oldFiber, newChild, lanes) {356 // Update the fiber if the keys match, otherwise return null.357 var key = oldFiber !== null ? oldFiber.key : null;358 if (typeof newChild === 'string' || typeof newChild === 'number') {359 // Text nodes don't have keys. If the previous node is implicitly keyed360 // we can continue to replace it without aborting even if it is not a text361 // node.362 if (key !== null) {363 return null;364 }365 return updateTextNode(returnFiber, oldFiber, '' + newChild, lanes);366 }367 if (typeof newChild === 'object' && newChild !== null) {368 switch (newChild.$$typeof) {369 case REACT_ELEMENT_TYPE:370 {371 if (newChild.key === key) {372 if (newChild.type === REACT_FRAGMENT_TYPE) {373 return updateFragment(returnFiber, oldFiber, newChild.props.children, lanes, key);374 }375 return updateElement(returnFiber, oldFiber, newChild, lanes);376 } else {377 return null;378 }379 }380 case REACT_PORTAL_TYPE:381 {382 if (newChild.key === key) {383 return updatePortal(returnFiber, oldFiber, newChild, lanes);384 } else {385 return null;386 }387 }388 case REACT_LAZY_TYPE:389 {390 {391 var payload = newChild._payload;392 var init = newChild._init;393 return updateSlot(returnFiber, oldFiber, init(payload), lanes);394 }395 }396 }397 if (isArray$1(newChild) || getIteratorFn(newChild)) {398 if (key !== null) {399 return null;400 }401 return updateFragment(returnFiber, oldFiber, newChild, lanes, null);402 }403 throwOnInvalidObjectType(returnFiber, newChild);404 }405 {406 if (typeof newChild === 'function') {407 warnOnFunctionType(returnFiber);408 }409 }410 return null;411 }412 function updateFromMap(existingChildren, returnFiber, newIdx, newChild, lanes) {413 if (typeof newChild === 'string' || typeof newChild === 'number') {414 // Text nodes don't have keys, so we neither have to check the old nor415 // new node for the key. If both are text nodes, they match.416 var matchedFiber = existingChildren.get(newIdx) || null;417 return updateTextNode(returnFiber, matchedFiber, '' + newChild, lanes);418 }419 if (typeof newChild === 'object' && newChild !== null) {420 switch (newChild.$$typeof) {421 case REACT_ELEMENT_TYPE:422 {423 var _matchedFiber = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;424 if (newChild.type === REACT_FRAGMENT_TYPE) {425 return updateFragment(returnFiber, _matchedFiber, newChild.props.children, lanes, newChild.key);426 }427 return updateElement(returnFiber, _matchedFiber, newChild, lanes);428 }429 case REACT_PORTAL_TYPE:430 {431 var _matchedFiber2 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;432 return updatePortal(returnFiber, _matchedFiber2, newChild, lanes);433 }434 case REACT_LAZY_TYPE:435 {436 var payload = newChild._payload;437 var init = newChild._init;438 return updateFromMap(existingChildren, returnFiber, newIdx, init(payload), lanes);439 }440 }441 if (isArray$1(newChild) || getIteratorFn(newChild)) {442 var _matchedFiber3 = existingChildren.get(newIdx) || null;443 return updateFragment(returnFiber, _matchedFiber3, newChild, lanes, null);444 }445 throwOnInvalidObjectType(returnFiber, newChild);446 }447 {448 if (typeof newChild === 'function') {449 warnOnFunctionType(returnFiber);450 }451 }452 return null;453 }454 /**455 * Warns if there is a duplicate or missing key456 */457 function warnOnInvalidKey(child, knownKeys, returnFiber) {458 {459 if (typeof child !== 'object' || child === null) {460 return knownKeys;461 }462 switch (child.$$typeof) {463 case REACT_ELEMENT_TYPE:464 case REACT_PORTAL_TYPE:465 warnForMissingKey(child, returnFiber);466 var key = child.key;467 if (typeof key !== 'string') {468 break;469 }470 if (knownKeys === null) {471 knownKeys = new Set();472 knownKeys.add(key);473 break;474 }475 if (!knownKeys.has(key)) {476 knownKeys.add(key);477 break;478 }479 error('Encountered two children with the same key, `%s`. ' + 'Keys should be unique so that components maintain their identity ' + 'across updates. Non-unique keys may cause children to be ' + 'duplicated and/or omitted â the behavior is unsupported and ' + 'could change in a future version.', key);480 break;481 case REACT_LAZY_TYPE:482 {483 var payload = child._payload;484 var init = child._init;485 warnOnInvalidKey(init(payload), knownKeys, returnFiber);486 break;487 }488 }489 }490 return knownKeys;491 }492 function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, lanes) {493 // This algorithm can't optimize by searching from both ends since we494 // don't have backpointers on fibers. I'm trying to see how far we can get495 // with that model. If it ends up not being worth the tradeoffs, we can496 // add it later.497 // Even with a two ended optimization, we'd want to optimize for the case498 // where there are few changes and brute force the comparison instead of499 // going for the Map. It'd like to explore hitting that path first in500 // forward-only mode and only go for the Map once we notice that we need501 // lots of look ahead. This doesn't handle reversal as well as two ended502 // search but that's unusual. Besides, for the two ended optimization to503 // work on Iterables, we'd need to copy the whole set.504 // In this first iteration, we'll just live with hitting the bad case505 // (adding everything to a Map) in for every insert/move.506 // If you change this code, also update reconcileChildrenIterator() which507 // uses the same algorithm.508 {509 // First, validate keys.510 var knownKeys = null;511 for (var i = 0; i < newChildren.length; i++) {512 var child = newChildren[i];513 knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);514 }515 }516 var resultingFirstChild = null;517 var previousNewFiber = null;518 var oldFiber = currentFirstChild;519 var lastPlacedIndex = 0;520 var newIdx = 0;521 var nextOldFiber = null;522 for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {523 if (oldFiber.index > newIdx) {524 nextOldFiber = oldFiber;525 oldFiber = null;526 } else {527 nextOldFiber = oldFiber.sibling;528 }529 var newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], lanes);530 if (newFiber === null) {531 // TODO: This breaks on empty slots like null children. That's532 // unfortunate because it triggers the slow path all the time. We need533 // a better way to communicate whether this was a miss or null,534 // boolean, undefined, etc.535 if (oldFiber === null) {536 oldFiber = nextOldFiber;537 }538 break;539 }540 if (shouldTrackSideEffects) {541 if (oldFiber && newFiber.alternate === null) {542 // We matched the slot, but we didn't reuse the existing fiber, so we543 // need to delete the existing child.544 deleteChild(returnFiber, oldFiber);545 }546 }547 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);548 if (previousNewFiber === null) {549 // TODO: Move out of the loop. This only happens for the first run.550 resultingFirstChild = newFiber;551 } else {552 // TODO: Defer siblings if we're not at the right index for this slot.553 // I.e. if we had null values before, then we want to defer this554 // for each null value. However, we also don't want to call updateSlot555 // with the previous one.556 previousNewFiber.sibling = newFiber;557 }558 previousNewFiber = newFiber;559 oldFiber = nextOldFiber;560 }561 if (newIdx === newChildren.length) {562 // We've reached the end of the new children. We can delete the rest.563 deleteRemainingChildren(returnFiber, oldFiber);564 return resultingFirstChild;565 }566 if (oldFiber === null) {567 // If we don't have any more existing children we can choose a fast path568 // since the rest will all be insertions.569 for (; newIdx < newChildren.length; newIdx++) {570 var _newFiber = createChild(returnFiber, newChildren[newIdx], lanes);571 if (_newFiber === null) {572 continue;573 }574 lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx);575 if (previousNewFiber === null) {576 // TODO: Move out of the loop. This only happens for the first run.577 resultingFirstChild = _newFiber;578 } else {579 previousNewFiber.sibling = _newFiber;580 }581 previousNewFiber = _newFiber;582 }583 return resultingFirstChild;584 } // Add all children to a key map for quick lookups.585 var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves.586 for (; newIdx < newChildren.length; newIdx++) {587 var _newFiber2 = updateFromMap(existingChildren, returnFiber, newIdx, newChildren[newIdx], lanes);588 if (_newFiber2 !== null) {589 if (shouldTrackSideEffects) {590 if (_newFiber2.alternate !== null) {591 // The new fiber is a work in progress, but if there exists a592 // current, that means that we reused the fiber. We need to delete593 // it from the child list so that we don't add it to the deletion594 // list.595 existingChildren.delete(_newFiber2.key === null ? newIdx : _newFiber2.key);596 }597 }598 lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx);599 if (previousNewFiber === null) {600 resultingFirstChild = _newFiber2;601 } else {602 previousNewFiber.sibling = _newFiber2;603 }604 previousNewFiber = _newFiber2;605 }606 }607 if (shouldTrackSideEffects) {608 // Any existing children that weren't consumed above were deleted. We need609 // to add them to the deletion list.610 existingChildren.forEach(function (child) {611 return deleteChild(returnFiber, child);612 });613 }614 return resultingFirstChild;615 }616 function reconcileChildrenIterator(returnFiber, currentFirstChild, newChildrenIterable, lanes) {617 // This is the same implementation as reconcileChildrenArray(),618 // but using the iterator instead.619 var iteratorFn = getIteratorFn(newChildrenIterable);620 if (!(typeof iteratorFn === 'function')) {621 {622 throw Error( "An object is not an iterable. This error is likely caused by a bug in React. Please file an issue." );623 }624 }625 {626 // We don't support rendering Generators because it's a mutation.627 // See https://github.com/facebook/react/issues/12995628 if (typeof Symbol === 'function' && // $FlowFixMe Flow doesn't know about toStringTag629 newChildrenIterable[Symbol.toStringTag] === 'Generator') {630 if (!didWarnAboutGenerators) {631 error('Using Generators as children is unsupported and will likely yield ' + 'unexpected results because enumerating a generator mutates it. ' + 'You may convert it to an array with `Array.from()` or the ' + '`[...spread]` operator before rendering. Keep in mind ' + 'you might need to polyfill these features for older browsers.');632 }633 didWarnAboutGenerators = true;634 } // Warn about using Maps as children635 if (newChildrenIterable.entries === iteratorFn) {636 if (!didWarnAboutMaps) {637 error('Using Maps as children is not supported. ' + 'Use an array of keyed ReactElements instead.');638 }639 didWarnAboutMaps = true;640 } // First, validate keys.641 // We'll get a different iterator later for the main pass.642 var _newChildren = iteratorFn.call(newChildrenIterable);643 if (_newChildren) {644 var knownKeys = null;645 var _step = _newChildren.next();646 for (; !_step.done; _step = _newChildren.next()) {647 var child = _step.value;648 knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);649 }650 }651 }652 var newChildren = iteratorFn.call(newChildrenIterable);653 if (!(newChildren != null)) {654 {655 throw Error( "An iterable object provided no iterator." );656 }657 }658 var resultingFirstChild = null;659 var previousNewFiber = null;660 var oldFiber = currentFirstChild;661 var lastPlacedIndex = 0;662 var newIdx = 0;663 var nextOldFiber = null;664 var step = newChildren.next();665 for (; oldFiber !== null && !step.done; newIdx++, step = newChildren.next()) {666 if (oldFiber.index > newIdx) {667 nextOldFiber = oldFiber;668 oldFiber = null;669 } else {670 nextOldFiber = oldFiber.sibling;671 }672 var newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);673 if (newFiber === null) {674 // TODO: This breaks on empty slots like null children. That's675 // unfortunate because it triggers the slow path all the time. We need676 // a better way to communicate whether this was a miss or null,677 // boolean, undefined, etc.678 if (oldFiber === null) {679 oldFiber = nextOldFiber;680 }681 break;682 }683 if (shouldTrackSideEffects) {684 if (oldFiber && newFiber.alternate === null) {685 // We matched the slot, but we didn't reuse the existing fiber, so we686 // need to delete the existing child.687 deleteChild(returnFiber, oldFiber);688 }689 }690 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);691 if (previousNewFiber === null) {692 // TODO: Move out of the loop. This only happens for the first run.693 resultingFirstChild = newFiber;694 } else {695 // TODO: Defer siblings if we're not at the right index for this slot.696 // I.e. if we had null values before, then we want to defer this697 // for each null value. However, we also don't want to call updateSlot698 // with the previous one.699 previousNewFiber.sibling = newFiber;700 }701 previousNewFiber = newFiber;702 oldFiber = nextOldFiber;703 }704 if (step.done) {705 // We've reached the end of the new children. We can delete the rest.706 deleteRemainingChildren(returnFiber, oldFiber);707 return resultingFirstChild;708 }709 if (oldFiber === null) {710 // If we don't have any more existing children we can choose a fast path711 // since the rest will all be insertions.712 for (; !step.done; newIdx++, step = newChildren.next()) {713 var _newFiber3 = createChild(returnFiber, step.value, lanes);714 if (_newFiber3 === null) {715 continue;716 }717 lastPlacedIndex = placeChild(_newFiber3, lastPlacedIndex, newIdx);718 if (previousNewFiber === null) {719 // TODO: Move out of the loop. This only happens for the first run.720 resultingFirstChild = _newFiber3;721 } else {722 previousNewFiber.sibling = _newFiber3;723 }724 previousNewFiber = _newFiber3;725 }726 return resultingFirstChild;727 } // Add all children to a key map for quick lookups.728 var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves.729 for (; !step.done; newIdx++, step = newChildren.next()) {730 var _newFiber4 = updateFromMap(existingChildren, returnFiber, newIdx, step.value, lanes);731 if (_newFiber4 !== null) {732 if (shouldTrackSideEffects) {733 if (_newFiber4.alternate !== null) {734 // The new fiber is a work in progress, but if there exists a735 // current, that means that we reused the fiber. We need to delete736 // it from the child list so that we don't add it to the deletion737 // list.738 existingChildren.delete(_newFiber4.key === null ? newIdx : _newFiber4.key);739 }740 }741 lastPlacedIndex = placeChild(_newFiber4, lastPlacedIndex, newIdx);742 if (previousNewFiber === null) {743 resultingFirstChild = _newFiber4;744 } else {745 previousNewFiber.sibling = _newFiber4;746 }747 previousNewFiber = _newFiber4;748 }749 }750 if (shouldTrackSideEffects) {751 // Any existing children that weren't consumed above were deleted. We need752 // to add them to the deletion list.753 existingChildren.forEach(function (child) {754 return deleteChild(returnFiber, child);755 });756 }757 return resultingFirstChild;758 }759 function reconcileSingleTextNode(returnFiber, currentFirstChild, textContent, lanes) {760 // There's no need to check for keys on text nodes since we don't have a761 // way to define them.762 if (currentFirstChild !== null && currentFirstChild.tag === HostText) {763 // We already have an existing node so let's just update it and delete764 // the rest.765 deleteRemainingChildren(returnFiber, currentFirstChild.sibling);766 var existing = useFiber(currentFirstChild, textContent);767 existing.return = returnFiber;768 return existing;769 } // The existing first child is not a text node so we need to create one770 // and delete the existing ones.771 deleteRemainingChildren(returnFiber, currentFirstChild);772 var created = createFiberFromText(textContent, returnFiber.mode, lanes);773 created.return = returnFiber;774 return created;775 }776 function reconcileSingleElement(returnFiber, currentFirstChild, element, lanes) {777 var key = element.key;778 var child = currentFirstChild;779 while (child !== null) {780 // TODO: If key === null and child.key === null, then this only applies to781 // the first item in the list.782 if (child.key === key) {783 switch (child.tag) {784 case Fragment:785 {786 if (element.type === REACT_FRAGMENT_TYPE) {787 deleteRemainingChildren(returnFiber, child.sibling);788 var existing = useFiber(child, element.props.children);789 existing.return = returnFiber;790 {791 existing._debugSource = element._source;792 existing._debugOwner = element._owner;793 }794 return existing;795 }796 break;797 }798 case Block:799 {800 var type = element.type;801 if (type.$$typeof === REACT_LAZY_TYPE) {802 type = resolveLazyType(type);803 }804 if (type.$$typeof === REACT_BLOCK_TYPE) {805 // The new Block might not be initialized yet. We need to initialize806 // it in case initializing it turns out it would match.807 if (type._render === child.type._render) {808 deleteRemainingChildren(returnFiber, child.sibling);809 var _existing2 = useFiber(child, element.props);810 _existing2.type = type;811 _existing2.return = returnFiber;812 {813 _existing2._debugSource = element._source;814 _existing2._debugOwner = element._owner;815 }816 return _existing2;817 }818 }819 }820 // We intentionally fallthrough here if enableBlocksAPI is not on.821 // eslint-disable-next-lined no-fallthrough822 default:823 {824 if (child.elementType === element.type || ( // Keep this check inline so it only runs on the false path:825 isCompatibleFamilyForHotReloading(child, element) )) {826 deleteRemainingChildren(returnFiber, child.sibling);827 var _existing3 = useFiber(child, element.props);828 _existing3.ref = coerceRef(returnFiber, child, element);829 _existing3.return = returnFiber;830 {831 _existing3._debugSource = element._source;832 _existing3._debugOwner = element._owner;833 }834 return _existing3;835 }836 break;837 }838 } // Didn't match.839 deleteRemainingChildren(returnFiber, child);840 break;841 } else {842 deleteChild(returnFiber, child);843 }844 child = child.sibling;845 }846 if (element.type === REACT_FRAGMENT_TYPE) {847 var created = createFiberFromFragment(element.props.children, returnFiber.mode, lanes, element.key);848 created.return = returnFiber;849 return created;850 } else {851 var _created4 = createFiberFromElement(element, returnFiber.mode, lanes);852 _created4.ref = coerceRef(returnFiber, currentFirstChild, element);853 _created4.return = returnFiber;854 return _created4;855 }856 }857 function reconcileSinglePortal(returnFiber, currentFirstChild, portal, lanes) {858 var key = portal.key;859 var child = currentFirstChild;860 while (child !== null) {861 // TODO: If key === null and child.key === null, then this only applies to862 // the first item in the list.863 if (child.key === key) {864 if (child.tag === HostPortal && child.stateNode.containerInfo === portal.containerInfo && child.stateNode.implementation === portal.implementation) {865 deleteRemainingChildren(returnFiber, child.sibling);866 var existing = useFiber(child, portal.children || []);867 existing.return = returnFiber;868 return existing;869 } else {870 deleteRemainingChildren(returnFiber, child);871 break;872 }873 } else {874 deleteChild(returnFiber, child);875 }876 child = child.sibling;877 }878 var created = createFiberFromPortal(portal, returnFiber.mode, lanes);879 created.return = returnFiber;880 return created;881 } // This API will tag the children with the side-effect of the reconciliation882 // itself. They will be added to the side-effect list as we pass through the883 // children and the parent.884 function reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes) {885 // This function is not recursive.886 // If the top level item is an array, we treat it as a set of children,887 // not as a fragment. Nested arrays on the other hand will be treated as888 // fragment nodes. Recursion happens at the normal flow.889 // Handle top level unkeyed fragments as if they were arrays.890 // This leads to an ambiguity between <>{[...]}</> and <>...</>.891 // We treat the ambiguous cases above the same.892 var isUnkeyedTopLevelFragment = typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null;893 if (isUnkeyedTopLevelFragment) {894 newChild = newChild.props.children;895 } // Handle object types896 var isObject = typeof newChild === 'object' && newChild !== null;897 if (isObject) {898 switch (newChild.$$typeof) {899 case REACT_ELEMENT_TYPE:900 return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes));901 case REACT_PORTAL_TYPE:902 return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, lanes));903 case REACT_LAZY_TYPE:904 {905 var payload = newChild._payload;906 var init = newChild._init; // TODO: This function is supposed to be non-recursive.907 return reconcileChildFibers(returnFiber, currentFirstChild, init(payload), lanes);908 }909 }910 }911 if (typeof newChild === 'string' || typeof newChild === 'number') {912 return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, lanes));913 }914 if (isArray$1(newChild)) {915 return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, lanes);916 }917 if (getIteratorFn(newChild)) {918 return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, lanes);919 }920 if (isObject) {921 throwOnInvalidObjectType(returnFiber, newChild);922 }923 {924 if (typeof newChild === 'function') {925 warnOnFunctionType(returnFiber);926 }927 }928 if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {929 // If the new child is undefined, and the return fiber is a composite930 // component, throw an error. If Fiber return types are disabled,931 // we already threw above.932 switch (returnFiber.tag) {933 case ClassComponent:934 {935 {936 var instance = returnFiber.stateNode;937 if (instance.render._isMockFunction) {938 // We allow auto-mocks to proceed as if they're returning null.939 break;...
reconcile.js
Source:reconcile.js
...199 addendum,200 );201 }202}203function warnOnFunctionType() {204 const currentComponentErrorInfo =205 'Functions are not valid as a React child. This may happen if ' +206 'you return a Component instead of <Component /> from render. ' +207 'Or maybe you meant to call this function rather than return it.' +208 getCurrentFiberStackInDev();209 if (ownerHasFunctionTypeWarning[currentComponentErrorInfo]) {210 return;211 }212 ownerHasFunctionTypeWarning[currentComponentErrorInfo] = true;213 warning(214 false,215 'Functions are not valid as a React child. This may happen if ' +216 'you return a Component instead of <Component /> from render. ' +217 'Or maybe you meant to call this function rather than return it.',218 );219}220// This wrapper function exists because I expect to clone the code in each path221// to be able to optimize each path individually by branching early. This needs222// a compiler or we can do it manually. Helpers that don't need this branching223// live outside of this function.224function ChildReconciler(shouldTrackSideEffects) {225 function deleteChild(returnFiber: Fiber, childToDelete: Fiber): void {226 if (!shouldTrackSideEffects) {227 // Noop.228 return;229 }230 // Deletions are added in reversed order so we add it to the front.231 // At this point, the return fiber's effect list is empty except for232 // deletions, so we can just append the deletion to the list. The remaining233 // effects aren't added until the complete phase. Once we implement234 // resuming, this may not be true.235 const last = returnFiber.lastEffect;236 if (last !== null) {237 last.nextEffect = childToDelete;238 returnFiber.lastEffect = childToDelete;239 } else {240 returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;241 }242 childToDelete.nextEffect = null;243 childToDelete.effectTag = Deletion;244 }245 function deleteRemainingChildren(246 returnFiber: Fiber,247 currentFirstChild: Fiber | null,248 ): null {249 if (!shouldTrackSideEffects) {250 // Noop.251 return null;252 }253 // TODO: For the shouldClone case, this could be micro-optimized a bit by254 // assuming that after the first child we've already added everything.255 let childToDelete = currentFirstChild;256 while (childToDelete !== null) {257 deleteChild(returnFiber, childToDelete);258 childToDelete = childToDelete.sibling;259 }260 return null;261 }262 function mapRemainingChildren(263 returnFiber: Fiber,264 currentFirstChild: Fiber,265 ): Map<string | number, Fiber> {266 // Add the remaining children to a temporary map so that we can find them by267 // keys quickly. Implicit (null) keys get added to this set with their index268 // instead.269 const existingChildren: Map<string | number, Fiber> = new Map();270 let existingChild = currentFirstChild;271 while (existingChild !== null) {272 if (existingChild.key !== null) {273 existingChildren.set(existingChild.key, existingChild);274 } else {275 existingChildren.set(existingChild.index, existingChild);276 }277 existingChild = existingChild.sibling;278 }279 return existingChildren;280 }281 function useFiber(282 fiber: Fiber,283 pendingProps: mixed,284 expirationTime: ExpirationTime,285 ): Fiber {286 // We currently set sibling to null and index to 0 here because it is easy287 // to forget to do before returning it. E.g. for the single child case.288 const clone = createWorkInProgress(fiber, pendingProps, expirationTime);289 clone.index = 0;290 clone.sibling = null;291 return clone;292 }293 function placeChild(294 newFiber: Fiber,295 lastPlacedIndex: number,296 newIndex: number,297 ): number {298 newFiber.index = newIndex;299 if (!shouldTrackSideEffects) {300 // Noop.301 return lastPlacedIndex;302 }303 const current = newFiber.alternate;304 if (current !== null) {305 const oldIndex = current.index;306 if (oldIndex < lastPlacedIndex) {307 // This is a move.308 newFiber.effectTag = Placement;309 return lastPlacedIndex;310 } else {311 // This item can stay in place.312 return oldIndex;313 }314 } else {315 // This is an insertion.316 newFiber.effectTag = Placement;317 return lastPlacedIndex;318 }319 }320 function placeSingleChild(newFiber: Fiber): Fiber {321 // This is simpler for the single child case. We only need to do a322 // placement for inserting new children.323 if (shouldTrackSideEffects && newFiber.alternate === null) {324 newFiber.effectTag = Placement;325 }326 return newFiber;327 }328 function updateTextNode(329 returnFiber: Fiber,330 current: Fiber | null,331 textContent: string,332 expirationTime: ExpirationTime,333 ) {334 if (current === null || current.tag !== HostText) {335 // Insert336 const created = createFiberFromText(337 textContent,338 returnFiber.mode,339 expirationTime,340 );341 created.return = returnFiber;342 return created;343 } else {344 // Update345 const existing = useFiber(current, textContent, expirationTime);346 existing.return = returnFiber;347 return existing;348 }349 }350 function updateElement(351 returnFiber: Fiber,352 current: Fiber | null,353 element: ReactElement,354 expirationTime: ExpirationTime,355 ): Fiber {356 if (current !== null && current.elementType === element.type) {357 // Move based on index358 const existing = useFiber(current, element.props, expirationTime);359 existing.ref = coerceRef(returnFiber, current, element);360 existing.return = returnFiber;361 if (__DEV__) {362 existing._debugSource = element._source;363 existing._debugOwner = element._owner;364 }365 return existing;366 } else {367 // Insert368 const created = createFiberFromElement(369 element,370 returnFiber.mode,371 expirationTime,372 );373 created.ref = coerceRef(returnFiber, current, element);374 created.return = returnFiber;375 return created;376 }377 }378 function updatePortal(379 returnFiber: Fiber,380 current: Fiber | null,381 portal: ReactPortal,382 expirationTime: ExpirationTime,383 ): Fiber {384 if (385 current === null ||386 current.tag !== HostPortal ||387 current.stateNode.containerInfo !== portal.containerInfo ||388 current.stateNode.implementation !== portal.implementation389 ) {390 // Insert391 const created = createFiberFromPortal(392 portal,393 returnFiber.mode,394 expirationTime,395 );396 created.return = returnFiber;397 return created;398 } else {399 // Update400 const existing = useFiber(current, portal.children || [], expirationTime);401 existing.return = returnFiber;402 return existing;403 }404 }405 function updateFragment(406 returnFiber: Fiber,407 current: Fiber | null,408 fragment: Iterable<*>,409 expirationTime: ExpirationTime,410 key: null | string,411 ): Fiber {412 if (current === null || current.tag !== Fragment) {413 // Insert414 const created = createFiberFromFragment(415 fragment,416 returnFiber.mode,417 expirationTime,418 key,419 );420 created.return = returnFiber;421 return created;422 } else {423 // Update424 const existing = useFiber(current, fragment, expirationTime);425 existing.return = returnFiber;426 return existing;427 }428 }429 function createChild(430 returnFiber: Fiber,431 newChild: any,432 expirationTime: ExpirationTime,433 ): Fiber | null {434 if (typeof newChild === 'string' || typeof newChild === 'number') {435 // Text nodes don't have keys. If the previous node is implicitly keyed436 // we can continue to replace it without aborting even if it is not a text437 // node.438 const created = createFiberFromText(439 '' + newChild,440 returnFiber.mode,441 expirationTime,442 );443 created.return = returnFiber;444 return created;445 }446 if (typeof newChild === 'object' && newChild !== null) {447 switch (newChild.$$typeof) {448 case REACT_ELEMENT_TYPE: {449 const created = createFiberFromElement(450 newChild,451 returnFiber.mode,452 expirationTime,453 );454 created.ref = coerceRef(returnFiber, null, newChild);455 created.return = returnFiber;456 return created;457 }458 case REACT_PORTAL_TYPE: {459 const created = createFiberFromPortal(460 newChild,461 returnFiber.mode,462 expirationTime,463 );464 created.return = returnFiber;465 return created;466 }467 }468 if (isArray(newChild) || getIteratorFn(newChild)) {469 const created = createFiberFromFragment(470 newChild,471 returnFiber.mode,472 expirationTime,473 null,474 );475 created.return = returnFiber;476 return created;477 }478 throwOnInvalidObjectType(returnFiber, newChild);479 }480 if (__DEV__) {481 if (typeof newChild === 'function') {482 warnOnFunctionType();483 }484 }485 return null;486 }487 function updateSlot(488 returnFiber: Fiber,489 oldFiber: Fiber | null,490 newChild: any,491 expirationTime: ExpirationTime,492 ): Fiber | null {493 // Update the fiber if the keys match, otherwise return null.494 const key = oldFiber !== null ? oldFiber.key : null;495 if (typeof newChild === 'string' || typeof newChild === 'number') {496 // Text nodes don't have keys. If the previous node is implicitly keyed497 // we can continue to replace it without aborting even if it is not a text498 // node.499 if (key !== null) {500 return null;501 }502 return updateTextNode(503 returnFiber,504 oldFiber,505 '' + newChild,506 expirationTime,507 );508 }509 if (typeof newChild === 'object' && newChild !== null) {510 switch (newChild.$$typeof) {511 case REACT_ELEMENT_TYPE: {512 if (newChild.key === key) {513 if (newChild.type === REACT_FRAGMENT_TYPE) {514 return updateFragment(515 returnFiber,516 oldFiber,517 newChild.props.children,518 expirationTime,519 key,520 );521 }522 return updateElement(523 returnFiber,524 oldFiber,525 newChild,526 expirationTime,527 );528 } else {529 return null;530 }531 }532 case REACT_PORTAL_TYPE: {533 if (newChild.key === key) {534 return updatePortal(535 returnFiber,536 oldFiber,537 newChild,538 expirationTime,539 );540 } else {541 return null;542 }543 }544 }545 if (isArray(newChild) || getIteratorFn(newChild)) {546 if (key !== null) {547 return null;548 }549 return updateFragment(550 returnFiber,551 oldFiber,552 newChild,553 expirationTime,554 null,555 );556 }557 throwOnInvalidObjectType(returnFiber, newChild);558 }559 if (__DEV__) {560 if (typeof newChild === 'function') {561 warnOnFunctionType();562 }563 }564 return null;565 }566 function updateFromMap(567 existingChildren: Map<string | number, Fiber>,568 returnFiber: Fiber,569 newIdx: number,570 newChild: any,571 expirationTime: ExpirationTime,572 ): Fiber | null {573 if (typeof newChild === 'string' || typeof newChild === 'number') {574 // Text nodes don't have keys, so we neither have to check the old nor575 // new node for the key. If both are text nodes, they match.576 const matchedFiber = existingChildren.get(newIdx) || null;577 return updateTextNode(578 returnFiber,579 matchedFiber,580 '' + newChild,581 expirationTime,582 );583 }584 if (typeof newChild === 'object' && newChild !== null) {585 switch (newChild.$$typeof) {586 case REACT_ELEMENT_TYPE: {587 const matchedFiber =588 existingChildren.get(589 newChild.key === null ? newIdx : newChild.key,590 ) || null;591 if (newChild.type === REACT_FRAGMENT_TYPE) {592 return updateFragment(593 returnFiber,594 matchedFiber,595 newChild.props.children,596 expirationTime,597 newChild.key,598 );599 }600 return updateElement(601 returnFiber,602 matchedFiber,603 newChild,604 expirationTime,605 );606 }607 case REACT_PORTAL_TYPE: {608 const matchedFiber =609 existingChildren.get(610 newChild.key === null ? newIdx : newChild.key,611 ) || null;612 return updatePortal(613 returnFiber,614 matchedFiber,615 newChild,616 expirationTime,617 );618 }619 }620 if (isArray(newChild) || getIteratorFn(newChild)) {621 const matchedFiber = existingChildren.get(newIdx) || null;622 return updateFragment(623 returnFiber,624 matchedFiber,625 newChild,626 expirationTime,627 null,628 );629 }630 throwOnInvalidObjectType(returnFiber, newChild);631 }632 if (__DEV__) {633 if (typeof newChild === 'function') {634 warnOnFunctionType();635 }636 }637 return null;638 }639 /**640 * Warns if there is a duplicate or missing key641 */642 function warnOnInvalidKey(643 child: mixed,644 knownKeys: Set<string> | null,645 ): Set<string> | null {646 if (__DEV__) {647 if (typeof child !== 'object' || child === null) {648 return knownKeys;649 }650 switch (child.$$typeof) {651 case REACT_ELEMENT_TYPE:652 case REACT_PORTAL_TYPE:653 warnForMissingKey(child);654 const key = child.key;655 if (typeof key !== 'string') {656 break;657 }658 if (knownKeys === null) {659 knownKeys = new Set();660 knownKeys.add(key);661 break;662 }663 if (!knownKeys.has(key)) {664 knownKeys.add(key);665 break;666 }667 warning(668 false,669 'Encountered two children with the same key, `%s`. ' +670 'Keys should be unique so that components maintain their identity ' +671 'across updates. Non-unique keys may cause children to be ' +672 'duplicated and/or omitted â the behavior is unsupported and ' +673 'could change in a future version.',674 key,675 );676 break;677 default:678 break;679 }680 }681 return knownKeys;682 }683 function reconcileChildrenArray(684 returnFiber: Fiber,685 currentFirstChild: Fiber | null,686 newChildren: Array<*>,687 expirationTime: ExpirationTime,688 ): Fiber | null {689 // This algorithm can't optimize by searching from both ends since we690 // don't have backpointers on fibers. I'm trying to see how far we can get691 // with that model. If it ends up not being worth the tradeoffs, we can692 // add it later.693 // Even with a two ended optimization, we'd want to optimize for the case694 // where there are few changes and brute force the comparison instead of695 // going for the Map. It'd like to explore hitting that path first in696 // forward-only mode and only go for the Map once we notice that we need697 // lots of look ahead. This doesn't handle reversal as well as two ended698 // search but that's unusual. Besides, for the two ended optimization to699 // work on Iterables, we'd need to copy the whole set.700 // In this first iteration, we'll just live with hitting the bad case701 // (adding everything to a Map) in for every insert/move.702 // If you change this code, also update reconcileChildrenIterator() which703 // uses the same algorithm.704 if (__DEV__) {705 // First, validate keys.706 let knownKeys = null;707 for (let i = 0; i < newChildren.length; i++) {708 const child = newChildren[i];709 knownKeys = warnOnInvalidKey(child, knownKeys);710 }711 }712 let resultingFirstChild: Fiber | null = null;713 let previousNewFiber: Fiber | null = null;714 let oldFiber = currentFirstChild;715 let lastPlacedIndex = 0;716 let newIdx = 0;717 let nextOldFiber = null;718 for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {719 if (oldFiber.index > newIdx) {720 nextOldFiber = oldFiber;721 oldFiber = null;722 } else {723 nextOldFiber = oldFiber.sibling;724 }725 const newFiber = updateSlot(726 returnFiber,727 oldFiber,728 newChildren[newIdx],729 expirationTime,730 );731 if (newFiber === null) {732 // TODO: This breaks on empty slots like null children. That's733 // unfortunate because it triggers the slow path all the time. We need734 // a better way to communicate whether this was a miss or null,735 // boolean, undefined, etc.736 if (oldFiber === null) {737 oldFiber = nextOldFiber;738 }739 break;740 }741 if (shouldTrackSideEffects) {742 if (oldFiber && newFiber.alternate === null) {743 // We matched the slot, but we didn't reuse the existing fiber, so we744 // need to delete the existing child.745 deleteChild(returnFiber, oldFiber);746 }747 }748 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);749 if (previousNewFiber === null) {750 // TODO: Move out of the loop. This only happens for the first run.751 resultingFirstChild = newFiber;752 } else {753 // TODO: Defer siblings if we're not at the right index for this slot.754 // I.e. if we had null values before, then we want to defer this755 // for each null value. However, we also don't want to call updateSlot756 // with the previous one.757 previousNewFiber.sibling = newFiber;758 }759 previousNewFiber = newFiber;760 oldFiber = nextOldFiber;761 }762 if (newIdx === newChildren.length) {763 // We've reached the end of the new children. We can delete the rest.764 deleteRemainingChildren(returnFiber, oldFiber);765 return resultingFirstChild;766 }767 if (oldFiber === null) {768 // If we don't have any more existing children we can choose a fast path769 // since the rest will all be insertions.770 for (; newIdx < newChildren.length; newIdx++) {771 const newFiber = createChild(772 returnFiber,773 newChildren[newIdx],774 expirationTime,775 );776 if (newFiber === null) {777 continue;778 }779 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);780 if (previousNewFiber === null) {781 // TODO: Move out of the loop. This only happens for the first run.782 resultingFirstChild = newFiber;783 } else {784 previousNewFiber.sibling = newFiber;785 }786 previousNewFiber = newFiber;787 }788 return resultingFirstChild;789 }790 // Add all children to a key map for quick lookups.791 const existingChildren = mapRemainingChildren(returnFiber, oldFiber);792 // Keep scanning and use the map to restore deleted items as moves.793 for (; newIdx < newChildren.length; newIdx++) {794 const newFiber = updateFromMap(795 existingChildren,796 returnFiber,797 newIdx,798 newChildren[newIdx],799 expirationTime,800 );801 if (newFiber !== null) {802 if (shouldTrackSideEffects) {803 if (newFiber.alternate !== null) {804 // The new fiber is a work in progress, but if there exists a805 // current, that means that we reused the fiber. We need to delete806 // it from the child list so that we don't add it to the deletion807 // list.808 existingChildren.delete(809 newFiber.key === null ? newIdx : newFiber.key,810 );811 }812 }813 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);814 if (previousNewFiber === null) {815 resultingFirstChild = newFiber;816 } else {817 previousNewFiber.sibling = newFiber;818 }819 previousNewFiber = newFiber;820 }821 }822 if (shouldTrackSideEffects) {823 // Any existing children that weren't consumed above were deleted. We need824 // to add them to the deletion list.825 existingChildren.forEach(child => deleteChild(returnFiber, child));826 }827 return resultingFirstChild;828 }829 function reconcileChildrenIterator(830 returnFiber: Fiber,831 currentFirstChild: Fiber | null,832 newChildrenIterable: Iterable<*>,833 expirationTime: ExpirationTime,834 ): Fiber | null {835 // This is the same implementation as reconcileChildrenArray(),836 // but using the iterator instead.837 const iteratorFn = getIteratorFn(newChildrenIterable);838 invariant(839 typeof iteratorFn === 'function',840 'An object is not an iterable. This error is likely caused by a bug in ' +841 'React. Please file an issue.',842 );843 if (__DEV__) {844 // We don't support rendering Generators because it's a mutation.845 // See https://github.com/facebook/react/issues/12995846 if (847 typeof Symbol === 'function' &&848 // $FlowFixMe Flow doesn't know about toStringTag849 newChildrenIterable[Symbol.toStringTag] === 'Generator'850 ) {851 warning(852 didWarnAboutGenerators,853 'Using Generators as children is unsupported and will likely yield ' +854 'unexpected results because enumerating a generator mutates it. ' +855 'You may convert it to an array with `Array.from()` or the ' +856 '`[...spread]` operator before rendering. Keep in mind ' +857 'you might need to polyfill these features for older browsers.',858 );859 didWarnAboutGenerators = true;860 }861 // Warn about using Maps as children862 if ((newChildrenIterable: any).entries === iteratorFn) {863 warning(864 didWarnAboutMaps,865 'Using Maps as children is unsupported and will likely yield ' +866 'unexpected results. Convert it to a sequence/iterable of keyed ' +867 'ReactElements instead.',868 );869 didWarnAboutMaps = true;870 }871 // First, validate keys.872 // We'll get a different iterator later for the main pass.873 const newChildren = iteratorFn.call(newChildrenIterable);874 if (newChildren) {875 let knownKeys = null;876 let step = newChildren.next();877 for (; !step.done; step = newChildren.next()) {878 const child = step.value;879 knownKeys = warnOnInvalidKey(child, knownKeys);880 }881 }882 }883 const newChildren = iteratorFn.call(newChildrenIterable);884 invariant(newChildren != null, 'An iterable object provided no iterator.');885 let resultingFirstChild: Fiber | null = null;886 let previousNewFiber: Fiber | null = null;887 let oldFiber = currentFirstChild;888 let lastPlacedIndex = 0;889 let newIdx = 0;890 let nextOldFiber = null;891 let step = newChildren.next();892 for (893 ;894 oldFiber !== null && !step.done;895 newIdx++, step = newChildren.next()896 ) {897 if (oldFiber.index > newIdx) {898 nextOldFiber = oldFiber;899 oldFiber = null;900 } else {901 nextOldFiber = oldFiber.sibling;902 }903 const newFiber = updateSlot(904 returnFiber,905 oldFiber,906 step.value,907 expirationTime,908 );909 if (newFiber === null) {910 // TODO: This breaks on empty slots like null children. That's911 // unfortunate because it triggers the slow path all the time. We need912 // a better way to communicate whether this was a miss or null,913 // boolean, undefined, etc.914 if (oldFiber === null) {915 oldFiber = nextOldFiber;916 }917 break;918 }919 if (shouldTrackSideEffects) {920 if (oldFiber && newFiber.alternate === null) {921 // We matched the slot, but we didn't reuse the existing fiber, so we922 // need to delete the existing child.923 deleteChild(returnFiber, oldFiber);924 }925 }926 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);927 if (previousNewFiber === null) {928 // TODO: Move out of the loop. This only happens for the first run.929 resultingFirstChild = newFiber;930 } else {931 // TODO: Defer siblings if we're not at the right index for this slot.932 // I.e. if we had null values before, then we want to defer this933 // for each null value. However, we also don't want to call updateSlot934 // with the previous one.935 previousNewFiber.sibling = newFiber;936 }937 previousNewFiber = newFiber;938 oldFiber = nextOldFiber;939 }940 if (step.done) {941 // We've reached the end of the new children. We can delete the rest.942 deleteRemainingChildren(returnFiber, oldFiber);943 return resultingFirstChild;944 }945 if (oldFiber === null) {946 // If we don't have any more existing children we can choose a fast path947 // since the rest will all be insertions.948 for (; !step.done; newIdx++, step = newChildren.next()) {949 const newFiber = createChild(returnFiber, step.value, expirationTime);950 if (newFiber === null) {951 continue;952 }953 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);954 if (previousNewFiber === null) {955 // TODO: Move out of the loop. This only happens for the first run.956 resultingFirstChild = newFiber;957 } else {958 previousNewFiber.sibling = newFiber;959 }960 previousNewFiber = newFiber;961 }962 return resultingFirstChild;963 }964 // Add all children to a key map for quick lookups.965 const existingChildren = mapRemainingChildren(returnFiber, oldFiber);966 // Keep scanning and use the map to restore deleted items as moves.967 for (; !step.done; newIdx++, step = newChildren.next()) {968 const newFiber = updateFromMap(969 existingChildren,970 returnFiber,971 newIdx,972 step.value,973 expirationTime,974 );975 if (newFiber !== null) {976 if (shouldTrackSideEffects) {977 if (newFiber.alternate !== null) {978 // The new fiber is a work in progress, but if there exists a979 // current, that means that we reused the fiber. We need to delete980 // it from the child list so that we don't add it to the deletion981 // list.982 existingChildren.delete(983 newFiber.key === null ? newIdx : newFiber.key,984 );985 }986 }987 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);988 if (previousNewFiber === null) {989 resultingFirstChild = newFiber;990 } else {991 previousNewFiber.sibling = newFiber;992 }993 previousNewFiber = newFiber;994 }995 }996 if (shouldTrackSideEffects) {997 // Any existing children that weren't consumed above were deleted. We need998 // to add them to the deletion list.999 existingChildren.forEach(child => deleteChild(returnFiber, child));1000 }1001 return resultingFirstChild;1002 }1003 function reconcileSingleTextNode(1004 returnFiber: Fiber,1005 currentFirstChild: Fiber | null,1006 textContent: string,1007 expirationTime: ExpirationTime,1008 ): Fiber {1009 // There's no need to check for keys on text nodes since we don't have a1010 // way to define them.1011 if (currentFirstChild !== null && currentFirstChild.tag === HostText) {1012 // We already have an existing node so let's just update it and delete1013 // the rest.1014 deleteRemainingChildren(returnFiber, currentFirstChild.sibling);1015 const existing = useFiber(currentFirstChild, textContent, expirationTime);1016 existing.return = returnFiber;1017 return existing;1018 }1019 // The existing first child is not a text node so we need to create one1020 // and delete the existing ones.1021 deleteRemainingChildren(returnFiber, currentFirstChild);1022 const created = createFiberFromText(1023 textContent,1024 returnFiber.mode,1025 expirationTime,1026 );1027 created.return = returnFiber;1028 return created;1029 }1030 function reconcileSingleElement(1031 returnFiber: Fiber,1032 currentFirstChild: Fiber | null,1033 element: ReactElement,1034 expirationTime: ExpirationTime,1035 ): Fiber {1036 const key = element.key;1037 let child = currentFirstChild;1038 while (child !== null) {1039 // TODO: If key === null and child.key === null, then this only applies to1040 // the first item in the list.1041 if (child.key === key) {1042 if (1043 child.tag === Fragment1044 ? element.type === REACT_FRAGMENT_TYPE1045 : child.elementType === element.type1046 ) {1047 deleteRemainingChildren(returnFiber, child.sibling);1048 const existing = useFiber(1049 child,1050 element.type === REACT_FRAGMENT_TYPE1051 ? element.props.children1052 : element.props,1053 expirationTime,1054 );1055 existing.ref = coerceRef(returnFiber, child, element);1056 existing.return = returnFiber;1057 if (__DEV__) {1058 existing._debugSource = element._source;1059 existing._debugOwner = element._owner;1060 }1061 return existing;1062 } else {1063 deleteRemainingChildren(returnFiber, child);1064 break;1065 }1066 } else {1067 deleteChild(returnFiber, child);1068 }1069 child = child.sibling;1070 }1071 if (element.type === REACT_FRAGMENT_TYPE) {1072 const created = createFiberFromFragment(1073 element.props.children,1074 returnFiber.mode,1075 expirationTime,1076 element.key,1077 );1078 created.return = returnFiber;1079 return created;1080 } else {1081 const created = createFiberFromElement(1082 element,1083 returnFiber.mode,1084 expirationTime,1085 );1086 created.ref = coerceRef(returnFiber, currentFirstChild, element);1087 created.return = returnFiber;1088 return created;1089 }1090 }1091 function reconcileSinglePortal(1092 returnFiber: Fiber,1093 currentFirstChild: Fiber | null,1094 portal: ReactPortal,1095 expirationTime: ExpirationTime,1096 ): Fiber {1097 const key = portal.key;1098 let child = currentFirstChild;1099 while (child !== null) {1100 // TODO: If key === null and child.key === null, then this only applies to1101 // the first item in the list.1102 if (child.key === key) {1103 if (1104 child.tag === HostPortal &&1105 child.stateNode.containerInfo === portal.containerInfo &&1106 child.stateNode.implementation === portal.implementation1107 ) {1108 deleteRemainingChildren(returnFiber, child.sibling);1109 const existing = useFiber(1110 child,1111 portal.children || [],1112 expirationTime,1113 );1114 existing.return = returnFiber;1115 return existing;1116 } else {1117 deleteRemainingChildren(returnFiber, child);1118 break;1119 }1120 } else {1121 deleteChild(returnFiber, child);1122 }1123 child = child.sibling;1124 }1125 const created = createFiberFromPortal(1126 portal,1127 returnFiber.mode,1128 expirationTime,1129 );1130 created.return = returnFiber;1131 return created;1132 }1133 // This API will tag the children with the side-effect of the reconciliation1134 // itself. They will be added to the side-effect list as we pass through the1135 // children and the parent.1136 function reconcileChildFibers(1137 returnFiber: Fiber,1138 currentFirstChild: Fiber | null,1139 newChild: any,1140 expirationTime: ExpirationTime,1141 ): Fiber | null {1142 // This function is not recursive.1143 // If the top level item is an array, we treat it as a set of children,1144 // not as a fragment. Nested arrays on the other hand will be treated as1145 // fragment nodes. Recursion happens at the normal flow.1146 // Handle top level unkeyed fragments as if they were arrays.1147 // This leads to an ambiguity between <>{[...]}</> and <>...</>.1148 // We treat the ambiguous cases above the same.1149 const isUnkeyedTopLevelFragment =1150 typeof newChild === 'object' &&1151 newChild !== null &&1152 newChild.type === REACT_FRAGMENT_TYPE &&1153 newChild.key === null;1154 if (isUnkeyedTopLevelFragment) {1155 newChild = newChild.props.children;1156 }1157 // Handle object types1158 const isObject = typeof newChild === 'object' && newChild !== null;1159 if (isObject) {1160 switch (newChild.$$typeof) {1161 case REACT_ELEMENT_TYPE:1162 return placeSingleChild(1163 reconcileSingleElement(1164 returnFiber,1165 currentFirstChild,1166 newChild,1167 expirationTime,1168 ),1169 );1170 case REACT_PORTAL_TYPE:1171 return placeSingleChild(1172 reconcileSinglePortal(1173 returnFiber,1174 currentFirstChild,1175 newChild,1176 expirationTime,1177 ),1178 );1179 }1180 }1181 if (typeof newChild === 'string' || typeof newChild === 'number') {1182 return placeSingleChild(1183 reconcileSingleTextNode(1184 returnFiber,1185 currentFirstChild,1186 '' + newChild,1187 expirationTime,1188 ),1189 );1190 }1191 if (isArray(newChild)) {1192 return reconcileChildrenArray(1193 returnFiber,1194 currentFirstChild,1195 newChild,1196 expirationTime,1197 );1198 }1199 if (getIteratorFn(newChild)) {1200 return reconcileChildrenIterator(1201 returnFiber,1202 currentFirstChild,1203 newChild,1204 expirationTime,1205 );1206 }1207 if (isObject) {1208 throwOnInvalidObjectType(returnFiber, newChild);1209 }1210 if (__DEV__) {1211 if (typeof newChild === 'function') {1212 warnOnFunctionType();1213 }1214 }1215 if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {1216 // If the new child is undefined, and the return fiber is a composite1217 // component, throw an error. If Fiber return types are disabled,1218 // we already threw above.1219 switch (returnFiber.tag) {1220 case ClassComponent: {1221 if (__DEV__) {1222 const instance = returnFiber.stateNode;1223 if (instance.render._isMockFunction) {1224 // We allow auto-mocks to proceed as if they're returning null.1225 break;1226 }...
ReactChildFiber.js
Source:ReactChildFiber.js
...190 addendum,191 );192 }193}194function warnOnFunctionType() {195 const currentComponentErrorInfo =196 'Functions are not valid as a React child. This may happen if ' +197 'you return a Component instead of <Component /> from render. ' +198 'Or maybe you meant to call this function rather than return it.' +199 (getCurrentFiberStackAddendum() || '');200 if (ownerHasFunctionTypeWarning[currentComponentErrorInfo]) {201 return;202 }203 ownerHasFunctionTypeWarning[currentComponentErrorInfo] = true;204 warning(205 false,206 'Functions are not valid as a React child. This may happen if ' +207 'you return a Component instead of <Component /> from render. ' +208 'Or maybe you meant to call this function rather than return it.%s',209 getCurrentFiberStackAddendum() || '',210 );211}212// This wrapper function exists because I expect to clone the code in each path213// to be able to optimize each path individually by branching early. This needs214// a compiler or we can do it manually. Helpers that don't need this branching215// live outside of this function.216function ChildReconciler(shouldTrackSideEffects) {217 function deleteChild(returnFiber: Fiber, childToDelete: Fiber): void {218 if (!shouldTrackSideEffects) {219 // Noop.220 return;221 }222 // Deletions are added in reversed order so we add it to the front.223 // At this point, the return fiber's effect list is empty except for224 // deletions, so we can just append the deletion to the list. The remaining225 // effects aren't added until the complete phase. Once we implement226 // resuming, this may not be true.227 const last = returnFiber.lastEffect;228 if (last !== null) {229 last.nextEffect = childToDelete;230 returnFiber.lastEffect = childToDelete;231 } else {232 returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;233 }234 childToDelete.nextEffect = null;235 childToDelete.effectTag = Deletion;236 }237 function deleteRemainingChildren(238 returnFiber: Fiber,239 currentFirstChild: Fiber | null,240 ): null {241 if (!shouldTrackSideEffects) {242 // Noop.243 return null;244 }245 // TODO: For the shouldClone case, this could be micro-optimized a bit by246 // assuming that after the first child we've already added everything.247 let childToDelete = currentFirstChild;248 while (childToDelete !== null) {249 deleteChild(returnFiber, childToDelete);250 childToDelete = childToDelete.sibling;251 }252 return null;253 }254 function mapRemainingChildren(255 returnFiber: Fiber,256 currentFirstChild: Fiber,257 ): Map<string | number, Fiber> {258 // Add the remaining children to a temporary map so that we can find them by259 // keys quickly. Implicit (null) keys get added to this set with their index260 // instead.261 const existingChildren: Map<string | number, Fiber> = new Map();262 let existingChild = currentFirstChild;263 while (existingChild !== null) {264 if (existingChild.key !== null) {265 existingChildren.set(existingChild.key, existingChild);266 } else {267 existingChildren.set(existingChild.index, existingChild);268 }269 existingChild = existingChild.sibling;270 }271 return existingChildren;272 }273 function useFiber(274 fiber: Fiber,275 pendingProps: mixed,276 expirationTime: ExpirationTime,277 ): Fiber {278 // We currently set sibling to null and index to 0 here because it is easy279 // to forget to do before returning it. E.g. for the single child case.280 const clone = createWorkInProgress(fiber, pendingProps, expirationTime);281 clone.index = 0;282 clone.sibling = null;283 return clone;284 }285 function placeChild(286 newFiber: Fiber,287 lastPlacedIndex: number,288 newIndex: number,289 ): number {290 newFiber.index = newIndex;291 if (!shouldTrackSideEffects) {292 // Noop.293 return lastPlacedIndex;294 }295 const current = newFiber.alternate;296 if (current !== null) {297 const oldIndex = current.index;298 if (oldIndex < lastPlacedIndex) {299 // This is a move.300 newFiber.effectTag = Placement;301 return lastPlacedIndex;302 } else {303 // This item can stay in place.304 return oldIndex;305 }306 } else {307 // This is an insertion.308 newFiber.effectTag = Placement;309 return lastPlacedIndex;310 }311 }312 function placeSingleChild(newFiber: Fiber): Fiber {313 // This is simpler for the single child case. We only need to do a314 // placement for inserting new children.315 if (shouldTrackSideEffects && newFiber.alternate === null) {316 newFiber.effectTag = Placement;317 }318 return newFiber;319 }320 function updateTextNode(321 returnFiber: Fiber,322 current: Fiber | null,323 textContent: string,324 expirationTime: ExpirationTime,325 ) {326 if (current === null || current.tag !== HostText) {327 // Insert328 const created = createFiberFromText(329 textContent,330 returnFiber.mode,331 expirationTime,332 );333 created.return = returnFiber;334 return created;335 } else {336 // Update337 const existing = useFiber(current, textContent, expirationTime);338 existing.return = returnFiber;339 return existing;340 }341 }342 function updateElement(343 returnFiber: Fiber,344 current: Fiber | null,345 element: ReactElement,346 expirationTime: ExpirationTime,347 ): Fiber {348 if (current !== null && current.type === element.type) {349 // Move based on index350 const existing = useFiber(current, element.props, expirationTime);351 existing.ref = coerceRef(returnFiber, current, element);352 existing.return = returnFiber;353 if (__DEV__) {354 existing._debugSource = element._source;355 existing._debugOwner = element._owner;356 }357 return existing;358 } else {359 // Insert360 const created = createFiberFromElement(361 element,362 returnFiber.mode,363 expirationTime,364 );365 created.ref = coerceRef(returnFiber, current, element);366 created.return = returnFiber;367 return created;368 }369 }370 function updatePortal(371 returnFiber: Fiber,372 current: Fiber | null,373 portal: ReactPortal,374 expirationTime: ExpirationTime,375 ): Fiber {376 if (377 current === null ||378 current.tag !== HostPortal ||379 current.stateNode.containerInfo !== portal.containerInfo ||380 current.stateNode.implementation !== portal.implementation381 ) {382 // Insert383 const created = createFiberFromPortal(384 portal,385 returnFiber.mode,386 expirationTime,387 );388 created.return = returnFiber;389 return created;390 } else {391 // Update392 const existing = useFiber(current, portal.children || [], expirationTime);393 existing.return = returnFiber;394 return existing;395 }396 }397 function updateFragment(398 returnFiber: Fiber,399 current: Fiber | null,400 fragment: Iterable<*>,401 expirationTime: ExpirationTime,402 key: null | string,403 ): Fiber {404 if (current === null || current.tag !== Fragment) {405 // Insert406 const created = createFiberFromFragment(407 fragment,408 returnFiber.mode,409 expirationTime,410 key,411 );412 created.return = returnFiber;413 return created;414 } else {415 // Update416 const existing = useFiber(current, fragment, expirationTime);417 existing.return = returnFiber;418 return existing;419 }420 }421 function createChild(422 returnFiber: Fiber,423 newChild: any,424 expirationTime: ExpirationTime,425 ): Fiber | null {426 if (typeof newChild === 'string' || typeof newChild === 'number') {427 // Text nodes don't have keys. If the previous node is implicitly keyed428 // we can continue to replace it without aborting even if it is not a text429 // node.430 const created = createFiberFromText(431 '' + newChild,432 returnFiber.mode,433 expirationTime,434 );435 created.return = returnFiber;436 return created;437 }438 if (typeof newChild === 'object' && newChild !== null) {439 switch (newChild.$$typeof) {440 case REACT_ELEMENT_TYPE: {441 const created = createFiberFromElement(442 newChild,443 returnFiber.mode,444 expirationTime,445 );446 created.ref = coerceRef(returnFiber, null, newChild);447 created.return = returnFiber;448 return created;449 }450 case REACT_PORTAL_TYPE: {451 const created = createFiberFromPortal(452 newChild,453 returnFiber.mode,454 expirationTime,455 );456 created.return = returnFiber;457 return created;458 }459 }460 if (isArray(newChild) || getIteratorFn(newChild)) {461 const created = createFiberFromFragment(462 newChild,463 returnFiber.mode,464 expirationTime,465 null,466 );467 created.return = returnFiber;468 return created;469 }470 throwOnInvalidObjectType(returnFiber, newChild);471 }472 if (__DEV__) {473 if (typeof newChild === 'function') {474 warnOnFunctionType();475 }476 }477 return null;478 }479 function updateSlot(480 returnFiber: Fiber,481 oldFiber: Fiber | null,482 newChild: any,483 expirationTime: ExpirationTime,484 ): Fiber | null {485 // Update the fiber if the keys match, otherwise return null.486 const key = oldFiber !== null ? oldFiber.key : null;487 if (typeof newChild === 'string' || typeof newChild === 'number') {488 // Text nodes don't have keys. If the previous node is implicitly keyed489 // we can continue to replace it without aborting even if it is not a text490 // node.491 if (key !== null) {492 return null;493 }494 return updateTextNode(495 returnFiber,496 oldFiber,497 '' + newChild,498 expirationTime,499 );500 }501 if (typeof newChild === 'object' && newChild !== null) {502 switch (newChild.$$typeof) {503 case REACT_ELEMENT_TYPE: {504 if (newChild.key === key) {505 if (newChild.type === REACT_FRAGMENT_TYPE) {506 return updateFragment(507 returnFiber,508 oldFiber,509 newChild.props.children,510 expirationTime,511 key,512 );513 }514 return updateElement(515 returnFiber,516 oldFiber,517 newChild,518 expirationTime,519 );520 } else {521 return null;522 }523 }524 case REACT_PORTAL_TYPE: {525 if (newChild.key === key) {526 return updatePortal(527 returnFiber,528 oldFiber,529 newChild,530 expirationTime,531 );532 } else {533 return null;534 }535 }536 }537 if (isArray(newChild) || getIteratorFn(newChild)) {538 if (key !== null) {539 return null;540 }541 return updateFragment(542 returnFiber,543 oldFiber,544 newChild,545 expirationTime,546 null,547 );548 }549 throwOnInvalidObjectType(returnFiber, newChild);550 }551 if (__DEV__) {552 if (typeof newChild === 'function') {553 warnOnFunctionType();554 }555 }556 return null;557 }558 function updateFromMap(559 existingChildren: Map<string | number, Fiber>,560 returnFiber: Fiber,561 newIdx: number,562 newChild: any,563 expirationTime: ExpirationTime,564 ): Fiber | null {565 if (typeof newChild === 'string' || typeof newChild === 'number') {566 // Text nodes don't have keys, so we neither have to check the old nor567 // new node for the key. If both are text nodes, they match.568 const matchedFiber = existingChildren.get(newIdx) || null;569 return updateTextNode(570 returnFiber,571 matchedFiber,572 '' + newChild,573 expirationTime,574 );575 }576 if (typeof newChild === 'object' && newChild !== null) {577 switch (newChild.$$typeof) {578 case REACT_ELEMENT_TYPE: {579 const matchedFiber =580 existingChildren.get(581 newChild.key === null ? newIdx : newChild.key,582 ) || null;583 if (newChild.type === REACT_FRAGMENT_TYPE) {584 return updateFragment(585 returnFiber,586 matchedFiber,587 newChild.props.children,588 expirationTime,589 newChild.key,590 );591 }592 return updateElement(593 returnFiber,594 matchedFiber,595 newChild,596 expirationTime,597 );598 }599 case REACT_PORTAL_TYPE: {600 const matchedFiber =601 existingChildren.get(602 newChild.key === null ? newIdx : newChild.key,603 ) || null;604 return updatePortal(605 returnFiber,606 matchedFiber,607 newChild,608 expirationTime,609 );610 }611 }612 if (isArray(newChild) || getIteratorFn(newChild)) {613 const matchedFiber = existingChildren.get(newIdx) || null;614 return updateFragment(615 returnFiber,616 matchedFiber,617 newChild,618 expirationTime,619 null,620 );621 }622 throwOnInvalidObjectType(returnFiber, newChild);623 }624 if (__DEV__) {625 if (typeof newChild === 'function') {626 warnOnFunctionType();627 }628 }629 return null;630 }631 /**632 * Warns if there is a duplicate or missing key633 */634 function warnOnInvalidKey(635 child: mixed,636 knownKeys: Set<string> | null,637 ): Set<string> | null {638 if (__DEV__) {639 if (typeof child !== 'object' || child === null) {640 return knownKeys;641 }642 switch (child.$$typeof) {643 case REACT_ELEMENT_TYPE:644 case REACT_PORTAL_TYPE:645 warnForMissingKey(child);646 const key = child.key;647 if (typeof key !== 'string') {648 break;649 }650 if (knownKeys === null) {651 knownKeys = new Set();652 knownKeys.add(key);653 break;654 }655 if (!knownKeys.has(key)) {656 knownKeys.add(key);657 break;658 }659 warning(660 false,661 'Encountered two children with the same key, `%s`. ' +662 'Keys should be unique so that components maintain their identity ' +663 'across updates. Non-unique keys may cause children to be ' +664 'duplicated and/or omitted â the behavior is unsupported and ' +665 'could change in a future version.%s',666 key,667 getCurrentFiberStackAddendum(),668 );669 break;670 default:671 break;672 }673 }674 return knownKeys;675 }676 function reconcileChildrenArray(677 returnFiber: Fiber,678 currentFirstChild: Fiber | null,679 newChildren: Array<*>,680 expirationTime: ExpirationTime,681 ): Fiber | null {682 // This algorithm can't optimize by searching from boths ends since we683 // don't have backpointers on fibers. I'm trying to see how far we can get684 // with that model. If it ends up not being worth the tradeoffs, we can685 // add it later.686 // Even with a two ended optimization, we'd want to optimize for the case687 // where there are few changes and brute force the comparison instead of688 // going for the Map. It'd like to explore hitting that path first in689 // forward-only mode and only go for the Map once we notice that we need690 // lots of look ahead. This doesn't handle reversal as well as two ended691 // search but that's unusual. Besides, for the two ended optimization to692 // work on Iterables, we'd need to copy the whole set.693 // In this first iteration, we'll just live with hitting the bad case694 // (adding everything to a Map) in for every insert/move.695 // If you change this code, also update reconcileChildrenIterator() which696 // uses the same algorithm.697 if (__DEV__) {698 // First, validate keys.699 let knownKeys = null;700 for (let i = 0; i < newChildren.length; i++) {701 const child = newChildren[i];702 knownKeys = warnOnInvalidKey(child, knownKeys);703 }704 }705 let resultingFirstChild: Fiber | null = null;706 let previousNewFiber: Fiber | null = null;707 let oldFiber = currentFirstChild;708 let lastPlacedIndex = 0;709 let newIdx = 0;710 let nextOldFiber = null;711 for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {712 if (oldFiber.index > newIdx) {713 nextOldFiber = oldFiber;714 oldFiber = null;715 } else {716 nextOldFiber = oldFiber.sibling;717 }718 const newFiber = updateSlot(719 returnFiber,720 oldFiber,721 newChildren[newIdx],722 expirationTime,723 );724 if (newFiber === null) {725 // TODO: This breaks on empty slots like null children. That's726 // unfortunate because it triggers the slow path all the time. We need727 // a better way to communicate whether this was a miss or null,728 // boolean, undefined, etc.729 if (oldFiber === null) {730 oldFiber = nextOldFiber;731 }732 break;733 }734 if (shouldTrackSideEffects) {735 if (oldFiber && newFiber.alternate === null) {736 // We matched the slot, but we didn't reuse the existing fiber, so we737 // need to delete the existing child.738 deleteChild(returnFiber, oldFiber);739 }740 }741 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);742 if (previousNewFiber === null) {743 // TODO: Move out of the loop. This only happens for the first run.744 resultingFirstChild = newFiber;745 } else {746 // TODO: Defer siblings if we're not at the right index for this slot.747 // I.e. if we had null values before, then we want to defer this748 // for each null value. However, we also don't want to call updateSlot749 // with the previous one.750 previousNewFiber.sibling = newFiber;751 }752 previousNewFiber = newFiber;753 oldFiber = nextOldFiber;754 }755 if (newIdx === newChildren.length) {756 // We've reached the end of the new children. We can delete the rest.757 deleteRemainingChildren(returnFiber, oldFiber);758 return resultingFirstChild;759 }760 if (oldFiber === null) {761 // If we don't have any more existing children we can choose a fast path762 // since the rest will all be insertions.763 for (; newIdx < newChildren.length; newIdx++) {764 const newFiber = createChild(765 returnFiber,766 newChildren[newIdx],767 expirationTime,768 );769 if (!newFiber) {770 continue;771 }772 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);773 if (previousNewFiber === null) {774 // TODO: Move out of the loop. This only happens for the first run.775 resultingFirstChild = newFiber;776 } else {777 previousNewFiber.sibling = newFiber;778 }779 previousNewFiber = newFiber;780 }781 return resultingFirstChild;782 }783 // Add all children to a key map for quick lookups.784 const existingChildren = mapRemainingChildren(returnFiber, oldFiber);785 // Keep scanning and use the map to restore deleted items as moves.786 for (; newIdx < newChildren.length; newIdx++) {787 const newFiber = updateFromMap(788 existingChildren,789 returnFiber,790 newIdx,791 newChildren[newIdx],792 expirationTime,793 );794 if (newFiber) {795 if (shouldTrackSideEffects) {796 if (newFiber.alternate !== null) {797 // The new fiber is a work in progress, but if there exists a798 // current, that means that we reused the fiber. We need to delete799 // it from the child list so that we don't add it to the deletion800 // list.801 existingChildren.delete(802 newFiber.key === null ? newIdx : newFiber.key,803 );804 }805 }806 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);807 if (previousNewFiber === null) {808 resultingFirstChild = newFiber;809 } else {810 previousNewFiber.sibling = newFiber;811 }812 previousNewFiber = newFiber;813 }814 }815 if (shouldTrackSideEffects) {816 // Any existing children that weren't consumed above were deleted. We need817 // to add them to the deletion list.818 existingChildren.forEach(child => deleteChild(returnFiber, child));819 }820 return resultingFirstChild;821 }822 function reconcileChildrenIterator(823 returnFiber: Fiber,824 currentFirstChild: Fiber | null,825 newChildrenIterable: Iterable<*>,826 expirationTime: ExpirationTime,827 ): Fiber | null {828 // This is the same implementation as reconcileChildrenArray(),829 // but using the iterator instead.830 const iteratorFn = getIteratorFn(newChildrenIterable);831 invariant(832 typeof iteratorFn === 'function',833 'An object is not an iterable. This error is likely caused by a bug in ' +834 'React. Please file an issue.',835 );836 if (__DEV__) {837 // Warn about using Maps as children838 if ((newChildrenIterable: any).entries === iteratorFn) {839 warning(840 didWarnAboutMaps,841 'Using Maps as children is unsupported and will likely yield ' +842 'unexpected results. Convert it to a sequence/iterable of keyed ' +843 'ReactElements instead.%s',844 getCurrentFiberStackAddendum(),845 );846 didWarnAboutMaps = true;847 }848 // First, validate keys.849 // We'll get a different iterator later for the main pass.850 const newChildren = iteratorFn.call(newChildrenIterable);851 if (newChildren) {852 let knownKeys = null;853 let step = newChildren.next();854 for (; !step.done; step = newChildren.next()) {855 const child = step.value;856 knownKeys = warnOnInvalidKey(child, knownKeys);857 }858 }859 }860 const newChildren = iteratorFn.call(newChildrenIterable);861 invariant(newChildren != null, 'An iterable object provided no iterator.');862 let resultingFirstChild: Fiber | null = null;863 let previousNewFiber: Fiber | null = null;864 let oldFiber = currentFirstChild;865 let lastPlacedIndex = 0;866 let newIdx = 0;867 let nextOldFiber = null;868 let step = newChildren.next();869 for (870 ;871 oldFiber !== null && !step.done;872 newIdx++, step = newChildren.next()873 ) {874 if (oldFiber.index > newIdx) {875 nextOldFiber = oldFiber;876 oldFiber = null;877 } else {878 nextOldFiber = oldFiber.sibling;879 }880 const newFiber = updateSlot(881 returnFiber,882 oldFiber,883 step.value,884 expirationTime,885 );886 if (newFiber === null) {887 // TODO: This breaks on empty slots like null children. That's888 // unfortunate because it triggers the slow path all the time. We need889 // a better way to communicate whether this was a miss or null,890 // boolean, undefined, etc.891 if (!oldFiber) {892 oldFiber = nextOldFiber;893 }894 break;895 }896 if (shouldTrackSideEffects) {897 if (oldFiber && newFiber.alternate === null) {898 // We matched the slot, but we didn't reuse the existing fiber, so we899 // need to delete the existing child.900 deleteChild(returnFiber, oldFiber);901 }902 }903 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);904 if (previousNewFiber === null) {905 // TODO: Move out of the loop. This only happens for the first run.906 resultingFirstChild = newFiber;907 } else {908 // TODO: Defer siblings if we're not at the right index for this slot.909 // I.e. if we had null values before, then we want to defer this910 // for each null value. However, we also don't want to call updateSlot911 // with the previous one.912 previousNewFiber.sibling = newFiber;913 }914 previousNewFiber = newFiber;915 oldFiber = nextOldFiber;916 }917 if (step.done) {918 // We've reached the end of the new children. We can delete the rest.919 deleteRemainingChildren(returnFiber, oldFiber);920 return resultingFirstChild;921 }922 if (oldFiber === null) {923 // If we don't have any more existing children we can choose a fast path924 // since the rest will all be insertions.925 for (; !step.done; newIdx++, step = newChildren.next()) {926 const newFiber = createChild(returnFiber, step.value, expirationTime);927 if (newFiber === null) {928 continue;929 }930 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);931 if (previousNewFiber === null) {932 // TODO: Move out of the loop. This only happens for the first run.933 resultingFirstChild = newFiber;934 } else {935 previousNewFiber.sibling = newFiber;936 }937 previousNewFiber = newFiber;938 }939 return resultingFirstChild;940 }941 // Add all children to a key map for quick lookups.942 const existingChildren = mapRemainingChildren(returnFiber, oldFiber);943 // Keep scanning and use the map to restore deleted items as moves.944 for (; !step.done; newIdx++, step = newChildren.next()) {945 const newFiber = updateFromMap(946 existingChildren,947 returnFiber,948 newIdx,949 step.value,950 expirationTime,951 );952 if (newFiber !== null) {953 if (shouldTrackSideEffects) {954 if (newFiber.alternate !== null) {955 // The new fiber is a work in progress, but if there exists a956 // current, that means that we reused the fiber. We need to delete957 // it from the child list so that we don't add it to the deletion958 // list.959 existingChildren.delete(960 newFiber.key === null ? newIdx : newFiber.key,961 );962 }963 }964 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);965 if (previousNewFiber === null) {966 resultingFirstChild = newFiber;967 } else {968 previousNewFiber.sibling = newFiber;969 }970 previousNewFiber = newFiber;971 }972 }973 if (shouldTrackSideEffects) {974 // Any existing children that weren't consumed above were deleted. We need975 // to add them to the deletion list.976 existingChildren.forEach(child => deleteChild(returnFiber, child));977 }978 return resultingFirstChild;979 }980 function reconcileSingleTextNode(981 returnFiber: Fiber,982 currentFirstChild: Fiber | null,983 textContent: string,984 expirationTime: ExpirationTime,985 ): Fiber {986 // There's no need to check for keys on text nodes since we don't have a987 // way to define them.988 if (currentFirstChild !== null && currentFirstChild.tag === HostText) {989 // We already have an existing node so let's just update it and delete990 // the rest.991 deleteRemainingChildren(returnFiber, currentFirstChild.sibling);992 const existing = useFiber(currentFirstChild, textContent, expirationTime);993 existing.return = returnFiber;994 return existing;995 }996 // The existing first child is not a text node so we need to create one997 // and delete the existing ones.998 deleteRemainingChildren(returnFiber, currentFirstChild);999 const created = createFiberFromText(1000 textContent,1001 returnFiber.mode,1002 expirationTime,1003 );1004 created.return = returnFiber;1005 return created;1006 }1007 function reconcileSingleElement(1008 returnFiber: Fiber,1009 currentFirstChild: Fiber | null,1010 element: ReactElement,1011 expirationTime: ExpirationTime,1012 ): Fiber {1013 const key = element.key;1014 let child = currentFirstChild;1015 while (child !== null) {1016 // TODO: If key === null and child.key === null, then this only applies to1017 // the first item in the list.1018 if (child.key === key) {1019 if (1020 child.tag === Fragment1021 ? element.type === REACT_FRAGMENT_TYPE1022 : child.type === element.type1023 ) {1024 deleteRemainingChildren(returnFiber, child.sibling);1025 const existing = useFiber(1026 child,1027 element.type === REACT_FRAGMENT_TYPE1028 ? element.props.children1029 : element.props,1030 expirationTime,1031 );1032 existing.ref = coerceRef(returnFiber, child, element);1033 existing.return = returnFiber;1034 if (__DEV__) {1035 existing._debugSource = element._source;1036 existing._debugOwner = element._owner;1037 }1038 return existing;1039 } else {1040 deleteRemainingChildren(returnFiber, child);1041 break;1042 }1043 } else {1044 deleteChild(returnFiber, child);1045 }1046 child = child.sibling;1047 }1048 if (element.type === REACT_FRAGMENT_TYPE) {1049 const created = createFiberFromFragment(1050 element.props.children,1051 returnFiber.mode,1052 expirationTime,1053 element.key,1054 );1055 created.return = returnFiber;1056 return created;1057 } else {1058 const created = createFiberFromElement(1059 element,1060 returnFiber.mode,1061 expirationTime,1062 );1063 created.ref = coerceRef(returnFiber, currentFirstChild, element);1064 created.return = returnFiber;1065 return created;1066 }1067 }1068 function reconcileSinglePortal(1069 returnFiber: Fiber,1070 currentFirstChild: Fiber | null,1071 portal: ReactPortal,1072 expirationTime: ExpirationTime,1073 ): Fiber {1074 const key = portal.key;1075 let child = currentFirstChild;1076 while (child !== null) {1077 // TODO: If key === null and child.key === null, then this only applies to1078 // the first item in the list.1079 if (child.key === key) {1080 if (1081 child.tag === HostPortal &&1082 child.stateNode.containerInfo === portal.containerInfo &&1083 child.stateNode.implementation === portal.implementation1084 ) {1085 deleteRemainingChildren(returnFiber, child.sibling);1086 const existing = useFiber(1087 child,1088 portal.children || [],1089 expirationTime,1090 );1091 existing.return = returnFiber;1092 return existing;1093 } else {1094 deleteRemainingChildren(returnFiber, child);1095 break;1096 }1097 } else {1098 deleteChild(returnFiber, child);1099 }1100 child = child.sibling;1101 }1102 const created = createFiberFromPortal(1103 portal,1104 returnFiber.mode,1105 expirationTime,1106 );1107 created.return = returnFiber;1108 return created;1109 }1110 // This API will tag the children with the side-effect of the reconciliation1111 // itself. They will be added to the side-effect list as we pass through the1112 // children and the parent.1113 function reconcileChildFibers(1114 returnFiber: Fiber,1115 currentFirstChild: Fiber | null,1116 newChild: any,1117 expirationTime: ExpirationTime,1118 ): Fiber | null {1119 // This function is not recursive.1120 // If the top level item is an array, we treat it as a set of children,1121 // not as a fragment. Nested arrays on the other hand will be treated as1122 // fragment nodes. Recursion happens at the normal flow.1123 // Handle top level unkeyed fragments as if they were arrays.1124 // This leads to an ambiguity between <>{[...]}</> and <>...</>.1125 // We treat the ambiguous cases above the same.1126 const isUnkeyedTopLevelFragment =1127 typeof newChild === 'object' &&1128 newChild !== null &&1129 newChild.type === REACT_FRAGMENT_TYPE &&1130 newChild.key === null;1131 if (isUnkeyedTopLevelFragment) {1132 newChild = newChild.props.children;1133 }1134 // Handle object types1135 const isObject = typeof newChild === 'object' && newChild !== null;1136 if (isObject) {1137 switch (newChild.$$typeof) {1138 case REACT_ELEMENT_TYPE:1139 return placeSingleChild(1140 reconcileSingleElement(1141 returnFiber,1142 currentFirstChild,1143 newChild,1144 expirationTime,1145 ),1146 );1147 case REACT_PORTAL_TYPE:1148 return placeSingleChild(1149 reconcileSinglePortal(1150 returnFiber,1151 currentFirstChild,1152 newChild,1153 expirationTime,1154 ),1155 );1156 }1157 }1158 if (typeof newChild === 'string' || typeof newChild === 'number') {1159 return placeSingleChild(1160 reconcileSingleTextNode(1161 returnFiber,1162 currentFirstChild,1163 '' + newChild,1164 expirationTime,1165 ),1166 );1167 }1168 if (isArray(newChild)) {1169 return reconcileChildrenArray(1170 returnFiber,1171 currentFirstChild,1172 newChild,1173 expirationTime,1174 );1175 }1176 if (getIteratorFn(newChild)) {1177 return reconcileChildrenIterator(1178 returnFiber,1179 currentFirstChild,1180 newChild,1181 expirationTime,1182 );1183 }1184 if (isObject) {1185 throwOnInvalidObjectType(returnFiber, newChild);1186 }1187 if (__DEV__) {1188 if (typeof newChild === 'function') {1189 warnOnFunctionType();1190 }1191 }1192 if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {1193 // If the new child is undefined, and the return fiber is a composite1194 // component, throw an error. If Fiber return types are disabled,1195 // we already threw above.1196 switch (returnFiber.tag) {1197 case ClassComponent: {1198 if (__DEV__) {1199 const instance = returnFiber.stateNode;1200 if (instance.render._isMockFunction) {1201 // We allow auto-mocks to proceed as if they're returning null.1202 break;1203 }...
reconcileChildren.js
Source:reconcileChildren.js
...75 }7677 {78 if (typeof newChild === 'function') {79 warnOnFunctionType();80 }81 }82 if (typeof newChild === 'undefined') {83 // If the new child is undefined, and the return fiber is a composite84 // component, throw an error. If Fiber return types are disabled,85 // we already threw above.86 switch (returnFiber.tag) {87 case ClassComponent:88 {89 {90 var instance = returnFiber.stateNode;91 if (instance.render._isMockFunction) {92 // We allow auto-mocks to proceed as if they're returning null.93 break;94 }95 }96 }97 // Intentionally fall through to the next case, which handles both98 // functions and classes99 // eslint-disable-next-lined no-fallthrough100 case FunctionalComponent:101 {102 var Component = returnFiber.type;103 invariant(false, '%s(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.', Component.displayName || Component.name || 'Component');104 }105 }106 }107108 // Remaining cases are all treated as empty.109 return deleteRemainingChildren(returnFiber, currentFirstChild);110}111112// è°åå个 react element113function reconcileSingleElement(returnFiber, currentFirstChild, element, expirationTime) {114 var key = element.key;115 var child = currentFirstChild;116117 // æ´æ°118 while (child !== null) {119 // TODO: If key === null and child.key === null, then this only applies to120 // the first item in the list.121 if (child.key === key) {122 if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.type === element.type) {123124 // key ç¸å type ç¸åï¼æ¸
é¤workInProgressé¤å½åfiberåèç¹å¤çææåèç¹125 deleteRemainingChildren(returnFiber, child.sibling);126127 // å建ä¸ä¸ªå½åchildçworkInProgress128 var existing = useFiber(child, element.type === REACT_FRAGMENT_TYPE ? element.props.children : element.props, expirationTime);129 existing.ref = coerceRef(returnFiber, child, element);130 existing['return'] = returnFiber;131 {132 existing._debugSource = element._source;133 existing._debugOwner = element._owner;134 }135 return existing;136 } else {137 // keyç¸åï¼typeä¸åï¼æ¸
é¤ææåèç¹138 deleteRemainingChildren(returnFiber, child);139 break;140 }141 } else {142 // å¦ækeyä¸ç¸åï¼ç´æ¥ä»å½åçworkInProgressæ¸
é¤å½åfiberåèç¹ï¼ç»§ç»å¤çä¸ä¸ä¸ªåèç¹143 deleteChild(returnFiber, child);144 }145 child = child.sibling;146 }147148 if (element.type === REACT_FRAGMENT_TYPE) {149 // fragment150 var created = createFiberFromFragment(element.props.children, returnFiber.mode, expirationTime, element.key);151 created['return'] = returnFiber;152 return created;153 } else {154155 // å建å级 fiber ../Fiber.js156 // æ ¹æ®åç»ä»¶ç±»åå建fiber157 var _created4 = createFiberFromElement(element, returnFiber.mode, expirationTime);158 // refç¸å
³ææ¶å¿½ç¥159 _created4.ref = coerceRef(returnFiber, currentFirstChild, element);160 // å
³èç¶çº§ fiberï¼æ¨¡æå½æ°æ è°ç¨ï¼å级å½æ°æ§è¡å®æï¼è°ç¨æ è¿åç¶çº§161 _created4['return'] = returnFiber;162 return _created4;163 }164}165166167168// è°åå¤ä¸ªå级fiberçæ´æ°,çç头æè涨æçæç½çç®æ³169// é¦æ¬¡æå
¥çæ¶åï¼ç´æ¥çæåèç¹ï¼æ·»å fiber.index ä¸è§£é170// æ´æ°çæ¶å171// ä»ç¬¬ä¸ä¸ªåèç¹å¼å§å¤çï¼è·æ°çchildListå表ä¸ç第ä¸ä¸ªå
ç´ æ¯è¾ï¼å¦ækeyå¹é
ï¼æ´æ°ï¼å¼å§å¤ç第äºä¸ªèç¹ï¼ä¾æ¬¡ç±»æ¨ï¼ä¸æ¦ä¸å¹é
ï¼è·³åºå¾ªç¯ï¼ä»å½åèç¹å¼å§ååï¼å
¨é¨æ·»å å°Mapå½ä¸ï¼éåmapåå©ä½çchildListï¼åå¨æ¢æ´æ°ï¼ä¸åå¨æ¢æ°å»ºï¼childListéåå®æä¹åï¼å¦æmapå½ä¸è¿åå¨åèç¹ï¼æ·»å å°å é¤å表172function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, expirationTime) {173 // This algorithm can't optimize by searching from boths ends since we174 // don't have backpointers on fibers. I'm trying to see how far we can get175 // with that model. If it ends up not being worth the tradeoffs, we can176 // add it later.177178 // Even with a two ended optimization, we'd want to optimize for the case179 // where there are few changes and brute force the comparison instead of180 // going for the Map. It'd like to explore hitting that path first in181 // forward-only mode and only go for the Map once we notice that we need182 // lots of look ahead. This doesn't handle reversal as well as two ended183 // search but that's unusual. Besides, for the two ended optimization to184 // work on Iterables, we'd need to copy the whole set.185186 // In this first iteration, we'll just live with hitting the bad case187 // (adding everything to a Map) in for every insert/move.188189 // If you change this code, also update reconcileChildrenIterator() which190 // uses the same algorithm.191192 {193 // First, validate keys.194 var knownKeys = null;195 for (var i = 0; i < newChildren.length; i++) {196 var child = newChildren[i];197 knownKeys = warnOnInvalidKey(child, knownKeys);198 }199 }200 201 var resultingFirstChild = null;202 var previousNewFiber = null;203204 var oldFiber = currentFirstChild;205 var lastPlacedIndex = 0;206 var newIdx = 0;207 var nextOldFiber = null;208209 // é¦æ¬¡æ¸²æçæ¶å oldFiber为nullï¼å¦å为 returnFiberç第ä¸ä¸ªåèç¹210 // ææfiberæ°å»ºçæ¶å index é½æ¯ 0,åªæå¨ reconcileChildrenArray æ reconcileChildrenIteratorä¹åï¼indexææå¯è½æ´æ¹211 for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {212 // å¦æoldFiberånewFiber index ç¸åæè¾å°ï¼è¯´ææ¯åä¸ä¸ªä½ç½®ï¼ç¸äºæ¯è¾ï¼å¦åè·³åºå¾ªç¯213 if (oldFiber.index > newIdx) {214 nextOldFiber = oldFiber;215 oldFiber = null;216 } else {217 nextOldFiber = oldFiber.sibling;218 }219 // 220 var newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], expirationTime);221 if (newFiber === null) {222 // TODO: This breaks on empty slots like null children. That's223 // unfortunate because it triggers the slow path all the time. We need224 // a better way to communicate whether this was a miss or null,225 // boolean, undefined, etc.226 if (oldFiber === null) {227 oldFiber = nextOldFiber;228 }229 break;230 }231 // 232 if (shouldTrackSideEffects) {233 if (oldFiber && newFiber.alternate === null) {234 // We matched the slot, but we didn't reuse the existing fiber, so we235 // need to delete the existing child.236 deleteChild(returnFiber, oldFiber);237 }238 }239 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);240 if (previousNewFiber === null) {241 // TODO: Move out of the loop. This only happens for the first run.242 resultingFirstChild = newFiber;243 } else {244 // TODO: Defer siblings if we're not at the right index for this slot.245 // I.e. if we had null values before, then we want to defer this246 // for each null value. However, we also don't want to call updateSlot247 // with the previous one.248 previousNewFiber.sibling = newFiber;249 }250 previousNewFiber = newFiber;251 oldFiber = nextOldFiber;252 }253254 if (newIdx === newChildren.length) {255 // We've reached the end of the new children. We can delete the rest.256 // å·²ç»å¤çå°æ°çåèç¹æ«å°¾ï¼å°oldFiberååé¢çå
å¼èç¹æ¸
é¤257 deleteRemainingChildren(returnFiber, oldFiber);258 return resultingFirstChild;259 }260261 // 没æåèç¹ï¼æè
oldFiberåå
çå
å¼åèç¹å
¨é¨æ´æ°å®æï¼ä»å½åçnewIndxå¼å§ï¼å°å©ä½çnewChildå
¨é¨æå
¥ï¼å¹¶æç
§å½åçæå
¥é¡ºåºèµå¼ fiber.index262 if (oldFiber === null) {263 // If we don't have any more existing children we can choose a fast path264 // since the rest will all be insertions.265266 // 267 for (; newIdx < newChildren.length; newIdx++) {268 var _newFiber = createChild(returnFiber, newChildren[newIdx], expirationTime);269 if (!_newFiber) {270 continue;271 }272 lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx);273 if (previousNewFiber === null) {274 // TODO: Move out of the loop. This only happens for the first run.275 resultingFirstChild = _newFiber;276 } else {277 previousNewFiber.sibling = _newFiber;278 }279 previousNewFiber = _newFiber;280 }281 return resultingFirstChild;282 }283284 // Add all children to a key map for quick lookups.285 var existingChildren = mapRemainingChildren(returnFiber, oldFiber);286287 // Keep scanning and use the map to restore deleted items as moves.288 for (; newIdx < newChildren.length; newIdx++) {289 var _newFiber2 = updateFromMap(existingChildren, returnFiber, newIdx, newChildren[newIdx], expirationTime);290 if (_newFiber2) {291 if (shouldTrackSideEffects) {292 if (_newFiber2.alternate !== null) {293 // The new fiber is a work in progress, but if there exists a294 // current, that means that we reused the fiber. We need to delete295 // it from the child list so that we don't add it to the deletion296 // list.297 existingChildren['delete'](_newFiber2.key === null ? newIdx : _newFiber2.key);298 }299 }300 lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx);301 if (previousNewFiber === null) {302 resultingFirstChild = _newFiber2;303 } else {304 previousNewFiber.sibling = _newFiber2;305 }306 previousNewFiber = _newFiber2;307 }308 }309310 if (shouldTrackSideEffects) {311 // Any existing children that weren't consumed above were deleted. We need312 // to add them to the deletion list.313 existingChildren.forEach(function (child) {314 return deleteChild(returnFiber, child);315 });316 }317318 return resultingFirstChild;319}320321// ä»mapä¸æ´æ°newChild322function updateFromMap(existingChildren, returnFiber, newIdx, newChild, expirationTime) {323 if (typeof newChild === 'string' || typeof newChild === 'number') {324 // Text nodes don't have keys, so we neither have to check the old nor325 // new node for the key. If both are text nodes, they match.326 var matchedFiber = existingChildren.get(newIdx) || null;327 return updateTextNode(returnFiber, matchedFiber, '' + newChild, expirationTime);328 }329330 if (typeof newChild === 'object' && newChild !== null) {331 switch (newChild.$$typeof) {332 case REACT_ELEMENT_TYPE:333 {334 var _matchedFiber = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;335 if (newChild.type === REACT_FRAGMENT_TYPE) {336 return updateFragment(returnFiber, _matchedFiber, newChild.props.children, expirationTime, newChild.key);337 }338 return updateElement(returnFiber, _matchedFiber, newChild, expirationTime);339 }340 case REACT_PORTAL_TYPE:341 {342 var _matchedFiber2 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;343 return updatePortal(returnFiber, _matchedFiber2, newChild, expirationTime);344 }345 }346347 if (isArray$1(newChild) || getIteratorFn(newChild)) {348 var _matchedFiber3 = existingChildren.get(newIdx) || null;349 return updateFragment(returnFiber, _matchedFiber3, newChild, expirationTime, null);350 }351352 throwOnInvalidObjectType(returnFiber, newChild);353 }354355 {356 if (typeof newChild === 'function') {357 warnOnFunctionType();358 }359 }360361 return null;362 }363364365 // å é¤fiberè°å366 function deleteChild(returnFiber, childToDelete) {367 if (!shouldTrackSideEffects) {368 // Noop.369 return;370 }371 // Deletions are added in reversed order so we add it to the front.372 // At this point, the return fiber's effect list is empty except for373 // deletions, so we can just append the deletion to the list. The remaining374 // effects aren't added until the complete phase. Once we implement375 // resuming, this may not be true.376377 // æå½åçfiberæ·»å å°returnFiberçå¯ä½ç¨é¾æ«å°¾ï¼ç±äºå¨å½åè°åº¦å¨å·¥ä½æ¶é´èç¹ï¼returnFiberçå¯ä½ç¨é¾å°¾ç©ºï¼å¨è°åç»æé¶æ®µï¼å³æ交çåä¸æ¥ï¼æä¼æå©ä½çå¯ä½ç¨æ·»å ï¼å æ¤ï¼å é¤æä½æç»ä¼å¨returnFiberçå¯ä½ç¨é¾é¦é¨378 var last = returnFiber.lastEffect;379 if (last !== null) {380 last.nextEffect = childToDelete;381 returnFiber.lastEffect = childToDelete;382 } else {383 returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;384 }385 childToDelete.nextEffect = null;386 childToDelete.effectTag = Deletion;387 }388389//å建fiberåèç¹390function createChild(returnFiber, newChild, expirationTime) {391 if (typeof newChild === 'string' || typeof newChild === 'number') {392 // Text nodes don't have keys. If the previous node is implicitly keyed393 // we can continue to replace it without aborting even if it is not a text394 // node.395 var created = createFiberFromText('' + newChild, returnFiber.mode, expirationTime);396 created['return'] = returnFiber;397 return created;398 }399400 if (typeof newChild === 'object' && newChild !== null) {401 switch (newChild.$$typeof) {402 case REACT_ELEMENT_TYPE:403 {404 var _created = createFiberFromElement(newChild, returnFiber.mode, expirationTime);405 _created.ref = coerceRef(returnFiber, null, newChild);406 _created['return'] = returnFiber;407 return _created;408 }409 case REACT_PORTAL_TYPE:410 {411 var _created2 = createFiberFromPortal(newChild, returnFiber.mode, expirationTime);412 _created2['return'] = returnFiber;413 return _created2;414 }415 }416417 if (isArray$1(newChild) || getIteratorFn(newChild)) {418 var _created3 = createFiberFromFragment(newChild, returnFiber.mode, expirationTime, null);419 _created3['return'] = returnFiber;420 return _created3;421 }422423 throwOnInvalidObjectType(returnFiber, newChild);424 }425426 {427 if (typeof newChild === 'function') {428 warnOnFunctionType();429 }430 }431432 return null;433}434435// å¤çåfiberçindexï¼æ£ç¡®ä½ç½®ï¼436function placeChild(newFiber, lastPlacedIndex, newIndex) {437 // èµå¼ fiber.index438 newFiber.index = newIndex;439 if (!shouldTrackSideEffects) {440 // ç´æ¥æå
¥ï¼ä¸éè¦ä»»ä½å¤ä½çæä½441 // Noop.442 return lastPlacedIndex;443 }444445 // å¨placeChildæ¹æ³å
ï¼fiberçå建æè
æ´æ°å±æ§é½å·²ç»å®æï¼æ以446 var current = newFiber.alternate;447448 if (current !== null) {449 // æ´æ° currentæ¯ç¨³å®çfiber450451 var oldIndex = current.index;452 if (oldIndex < lastPlacedIndex) {453 // This is a move.454 // éè¦åå移å¨,æ·»å å ä½å¯ä½ç¨455 newFiber.effectTag = Placement;456 return lastPlacedIndex;457 } else {458 // åæ¥ä½ç½®é åï¼ä¸ç¨å¤ç459 // This item can stay in place.460 return oldIndex;461 }462 } else {463 // This is an insertion.464 // æå
¥ 465 newFiber.effectTag = Placement;466 return lastPlacedIndex;467 }468}469470function updateSlot(returnFiber, oldFiber, newChild, expirationTime) {471 // Update the fiber if the keys match, otherwise return null.472473 // å¦ækeyå¹é
ï¼æ´æ°ï¼å
¶ä»æ
åµä¸åå¤ç474475 var key = oldFiber !== null ? oldFiber.key : null;476477 if (typeof newChild === 'string' || typeof newChild === 'number') {478 // Text nodes don't have keys. If the previous node is implicitly keyed479 // we can continue to replace it without aborting even if it is not a text480 // node.481482 // oldFilber åå¨key ï¼å¹¶ä¸ newChild æ¯ææ¬ï¼ç´æ¥è¿åï¼ä¸åå¤ç483 if (key !== null) {484 return null;485 }486 // oldFilber 没ækey ï¼å¹¶ä¸ newChild æ¯ææ¬ï¼ æ´æ°oldFiber487 return updateTextNode(returnFiber, oldFiber, '' + newChild, expirationTime);488 }489490 if (typeof newChild === 'object' && newChild !== null) {491 switch (newChild.$$typeof) {492 // react element493 case REACT_ELEMENT_TYPE:494 {495 // key ç¸åï¼æ´æ°496 if (newChild.key === key) {497 if (newChild.type === REACT_FRAGMENT_TYPE) {498 return updateFragment(returnFiber, oldFiber, newChild.props.children, expirationTime, key);499 }500 return updateElement(returnFiber, oldFiber, newChild, expirationTime);501 } else {502 // å¦å503 return null;504 }505 }506 case REACT_PORTAL_TYPE:507 {508 if (newChild.key === key) {509 return updatePortal(returnFiber, oldFiber, newChild, expirationTime);510 } else {511 return null;512 }513 }514 }515516 // æ°ç»åèç¹517 if (isArray$1(newChild) || getIteratorFn(newChild)) {518 if (key !== null) {519 return null;520 }521522 return updateFragment(returnFiber, oldFiber, newChild, expirationTime, null);523 }524525 throwOnInvalidObjectType(returnFiber, newChild);526 }527528 {529 if (typeof newChild === 'function') {530 warnOnFunctionType();531 }532 }533534 return null;535}536537// æ´æ°fiber538function updateElement(returnFiber, current, element, expirationTime) {539 if (current !== null && current.type === element.type) {540 // Move based on index541 var existing = useFiber(current, element.props, expirationTime);542 existing.ref = coerceRef(returnFiber, current, element);543 existing['return'] = returnFiber;544 {
...
Using AI Code Generation
1const { warnOnFunctionType } = require('playwright/lib/utils/stackTrace');2warnOnFunctionType();3const { chromium } = require('playwright');4(async () => {5 const browser = await chromium.launch();6 const context = await browser.newContext();7 const page = await context.newPage();8 await page.screenshot({ path: 'google.png' });9 await browser.close();10})();
Using AI Code Generation
1const { warnOnFunctionType } = require('@playwright/test/lib/internal/stackTrace');2warnOnFunctionType();3const { warnOnFunctionType } = require('@playwright/test');4warnOnFunctionType();5const { warnOnFunctionType } = require('@playwright/test/lib/utils');6warnOnFunctionType();7const { warnOnFunctionType } = require('@playwright/test/lib/utils').utils;8warnOnFunctionType();9const { warnOnFunctionType } = require('@playwright/test/lib/utils').utils;10warnOnFunctionType();11const { warnOnFunctionType } = require('@playwright/test/lib/utils').utils;12warnOnFunctionType();13const { warnOnFunctionType } = require('@playwright/test/lib/utils').utils;14warnOnFunctionType();15const { warnOnFunctionType } = require('@playwright/test/lib/utils').utils;16warnOnFunctionType();17const { warnOnFunctionType } = require('@playwright/test/lib/utils').utils;18warnOnFunctionType();19const { warnOnFunctionType } = require('@playwright/test/lib/utils').utils;20warnOnFunctionType();21const { warnOnFunctionType } = require('@playwright/test/lib/utils').utils;22warnOnFunctionType();23const { warnOnFunctionType } = require('@playwright/test/lib/utils').utils;24warnOnFunctionType();25const { warnOnFunctionType } = require('@playwright/test/lib/utils').utils;26warnOnFunctionType();27const { warnOnFunctionType } = require('@playwright/test/lib/utils').utils;
Using AI Code Generation
1const { warnOnFunctionType } = require('playwright/lib/utils/stackTrace');2warnOnFunctionType();3(async () => {4 const browser = await chromium.launch();5 const page = await browser.newPage();6 await page.screenshot({ path: 'example.png' });7 await browser.close();8})();9const { warnOnFunctionType } = require('playwright/lib/utils/stackTrace');10warnOnFunctionType();
Using AI Code Generation
1const { warnOnFunctionType } = require('playwright/lib/utils/utils');2const { test } = require('@playwright/test');3test('my test', async ({ page }) => {4 warnOnFunctionType('my test', 'page.click', 'options', page.click, 3);5 await page.click('a', { timeout: 5000 });6});
Using AI Code Generation
1const { test } = require('@playwright/test');2const { warnOnFunctionType } = require('@playwright/test/lib/internal/inspector');3test('test', async ({ page }) => {4 warnOnFunctionType(page, 'functionType');5 await page.click('text=Get started');6 await page.click('text=Docs');7 await page.click('text=API');8 const elementHandle = await page.$('text=BrowserContext');9 await elementHandle.click();10 await page.click('text=class: Page');11 await page.click('text=method: Page.type');12 await page.click('text=method: Page.fill');13 await page.click('text=method: Page.selectOption');14 await page.click('text=method: Page.setInputFiles');15 await page.click('text=method: Page.check');16 await page.click('text=method: Page.uncheck');17 await page.click('text=method: Page.waitForFunction');18 await page.click('text=method: Page.waitForSelector');19 await page.click('text=method: Page.waitForXPath');20 await page.click('text=method: Page.waitForTimeout');21 await page.click('text=method: Page.waitForRequest');22 await page.click('text=method: Page.waitForResponse');23 await page.click('text=method: Page.waitForEvent');24 await page.click('text=method: Page.waitForLoadState');25 await page.click('text=method: Page.waitForURL');26 await page.click('text=method: Page.title');27 await page.click('text=method: Page.close');28 await page.click('text=method: Page.context');29 await page.click('text=method: Page.mainFrame');30 await page.click('text=method: Page.frames');31 await page.click('text=method: Page.frame');32 await page.click('text=method: Page.route');33 await page.click('text=method: Page.route');34 await page.click('text=method: Page.unroute');35 await page.click('text=method: Page.unr
Using AI Code Generation
1const { warnOnFunctionType } = require('@playwright/test/lib/utils/utils');2warnOnFunctionType('myFunction', myFunction);3const { warnOnFunctionType } = require('@playwright/test/lib/utils/utils');4warnOnFunctionType('myFunction', myFunction);5const { warnOnFunctionType } = require('@playwright/test/lib/utils/utils');6warnOnFunctionType('myFunction', myFunction);7const { warnOnFunctionType } = require('@playwright/test/lib/utils/utils');8warnOnFunctionType('myFunction', myFunction);9const { warnOnFunctionType } = require('@playwright/test/lib/utils/utils');10warnOnFunctionType('myFunction', myFunction);11const { warnOnFunctionType } = require('@playwright/test/lib/utils/utils');12warnOnFunctionType('myFunction', myFunction);13const { warnOnFunctionType } = require('@playwright/test/lib/utils/utils');14warnOnFunctionType('myFunction', myFunction);15const { warnOnFunctionType } = require('@playwright/test/lib/utils/utils');16warnOnFunctionType('myFunction', myFunction);17const { warnOnFunctionType } = require('@playwright/test/lib/utils/utils');18warnOnFunctionType('myFunction', myFunction);19const { warnOnFunctionType } = require('@playwright/test/lib/utils/utils');20warnOnFunctionType('myFunction', myFunction);21const { warnOnFunctionType } = require('@playwright/test/lib/utils/utils');22warnOnFunctionType('myFunction', myFunction);23const { warnOnFunctionType } = require('@playwright/test/lib/utils/utils');24warnOnFunctionType('myFunction
Using AI Code Generation
1const { warnOnFunctionType } = require('playwright/lib/server/supplements/recorder/recorderSupplement');2warnOnFunctionType('functionType');3const { warnOnFunctionType } = require('playwright/lib/server/supplements/recorder/recorderSupplement');4warnOnFunctionType('functionType');5const { warnOnFunctionType } = require('playwright/lib/server/supplements/recorder/recorderSupplement');6module.exports = { warnOnFunctionType };7const { warnOnFunctionType } = require('playwright/lib/server/supplements/recorder/recorderSupplement');8warnOnFunctionType('functionType');
Using AI Code Generation
1import { warnOnFunctionType } from 'playwright/lib/utils/utils.js';2warnOnFunctionType('argumentName', 'argumentType', arg);3import { warnOnFunctionType } from 'playwright/lib/utils/utils.js';4warnOnFunctionType('argumentName', 'argumentType', arg);5import { warnOnFunctionType } from 'playwright/lib/utils/utils.js';6warnOnFunctionType('argumentName', 'argumentType', arg);7import { warnOnFunctionType } from 'playwright/lib/utils/utils.js';8warnOnFunctionType('argumentName', 'argumentType', arg);9import { warnOnFunctionType } from 'playwright/lib/utils/utils.js';10warnOnFunctionType('argumentName', 'argumentType', arg);11import { warnOnFunctionType } from 'playwright/lib/utils/utils.js';12warnOnFunctionType('argumentName', 'argumentType', arg);13import { warnOnFunctionType } from 'playwright/lib/utils/utils.js';14warnOnFunctionType('argumentName', 'argumentType', arg);15import { warnOnFunctionType } from 'playwright/lib/utils/utils.js';16warnOnFunctionType('argumentName', 'argumentType', arg);17import { warnOnFunctionType } from 'playwright/lib/utils/utils.js';18warnOnFunctionType('argumentName', 'argumentType', arg);19import { warnOnFunctionType } from 'playwright/lib/utils/utils.js';20warnOnFunctionType('argumentName', 'argumentType', arg);21import { warnOnFunctionType } from 'playwright/lib/utils/utils.js';
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!