How to use warnOnFunctionType method in Playwright Internal

Best JavaScript code snippet using playwright-internal

ReactChildFiber.new.js

Source:ReactChildFiber.new.js Github

copy

Full Screen

...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 }...

Full Screen

Full Screen

ReactChildFiber.old.js

Source:ReactChildFiber.old.js Github

copy

Full Screen

...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;...

Full Screen

Full Screen

reconcile.js

Source:reconcile.js Github

copy

Full Screen

...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 }...

Full Screen

Full Screen

ReactChildFiber.js

Source:ReactChildFiber.js Github

copy

Full Screen

...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 }...

Full Screen

Full Screen

reconcileChildren.js

Source:reconcileChildren.js Github

copy

Full Screen

...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 { ...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

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})();

Full Screen

Using AI Code Generation

copy

Full Screen

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;

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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});

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Using AI Code Generation

copy

Full Screen

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');

Full Screen

Using AI Code Generation

copy

Full Screen

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';

Full Screen

Playwright tutorial

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.

Chapters:

  1. What is Playwright : Playwright is comparatively new but has gained good popularity. Get to know some history of the Playwright with some interesting facts connected with it.
  2. How To Install Playwright : Learn in detail about what basic configuration and dependencies are required for installing Playwright and run a test. Get a step-by-step direction for installing the Playwright automation framework.
  3. Playwright Futuristic Features: Launched in 2020, Playwright gained huge popularity quickly because of some obliging features such as Playwright Test Generator and Inspector, Playwright Reporter, Playwright auto-waiting mechanism and etc. Read up on those features to master Playwright testing.
  4. What is Component Testing: Component testing in Playwright is a unique feature that allows a tester to test a single component of a web application without integrating them with other elements. Learn how to perform Component testing on the Playwright automation framework.
  5. Inputs And Buttons In Playwright: Every website has Input boxes and buttons; learn about testing inputs and buttons with different scenarios and examples.
  6. Functions and Selectors in Playwright: Learn how to launch the Chromium browser with Playwright. Also, gain a better understanding of some important functions like “BrowserContext,” which allows you to run multiple browser sessions, and “newPage” which interacts with a page.
  7. Handling Alerts and Dropdowns in Playwright : Playwright interact with different types of alerts and pop-ups, such as simple, confirmation, and prompt, and different types of dropdowns, such as single selector and multi-selector get your hands-on with handling alerts and dropdown in Playright testing.
  8. Playwright vs Puppeteer: Get to know about the difference between two testing frameworks and how they are different than one another, which browsers they support, and what features they provide.
  9. Run Playwright Tests on LambdaTest: Playwright testing with LambdaTest leverages test performance to the utmost. You can run multiple Playwright tests in Parallel with the LammbdaTest test cloud. Get a step-by-step guide to run your Playwright test on the LambdaTest platform.
  10. Playwright Python Tutorial: Playwright automation framework support all major languages such as Python, JavaScript, TypeScript, .NET and etc. However, there are various advantages to Python end-to-end testing with Playwright because of its versatile utility. Get the hang of Playwright python testing with this chapter.
  11. Playwright End To End Testing Tutorial: Get your hands on with Playwright end-to-end testing and learn to use some exciting features such as TraceViewer, Debugging, Networking, Component testing, Visual testing, and many more.
  12. Playwright Video Tutorial: Watch the video tutorials on Playwright testing from experts and get a consecutive in-depth explanation of Playwright automation testing.

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful