diff options
author | Florian Dold <florian.dold@gmail.com> | 2017-05-03 15:35:00 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2017-05-03 15:35:00 +0200 |
commit | de98e0b232509d5f40c135d540a70e415272ff85 (patch) | |
tree | a79222a5b58484ab3b80d18efcaaa7ccc4769b33 /node_modules/react-dom/lib/ReactFiberBeginWork.js | |
parent | e0c9d480a73fa629c1e4a47d3e721f1d2d345406 (diff) |
node_modules
Diffstat (limited to 'node_modules/react-dom/lib/ReactFiberBeginWork.js')
-rw-r--r-- | node_modules/react-dom/lib/ReactFiberBeginWork.js | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/node_modules/react-dom/lib/ReactFiberBeginWork.js b/node_modules/react-dom/lib/ReactFiberBeginWork.js new file mode 100644 index 000000000..baa1197a6 --- /dev/null +++ b/node_modules/react-dom/lib/ReactFiberBeginWork.js @@ -0,0 +1,380 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + */ + +'use strict'; + +var _require = require('./ReactChildFiber'), + reconcileChildFibers = _require.reconcileChildFibers, + reconcileChildFibersInPlace = _require.reconcileChildFibersInPlace, + cloneChildFibers = _require.cloneChildFibers; + +var _require2 = require('./ReactPriorityLevel'), + LowPriority = _require2.LowPriority; + +var ReactTypeOfWork = require('./ReactTypeOfWork'); +var IndeterminateComponent = ReactTypeOfWork.IndeterminateComponent, + FunctionalComponent = ReactTypeOfWork.FunctionalComponent, + ClassComponent = ReactTypeOfWork.ClassComponent, + HostContainer = ReactTypeOfWork.HostContainer, + HostComponent = ReactTypeOfWork.HostComponent, + CoroutineComponent = ReactTypeOfWork.CoroutineComponent, + CoroutineHandlerPhase = ReactTypeOfWork.CoroutineHandlerPhase, + YieldComponent = ReactTypeOfWork.YieldComponent; + +var _require3 = require('./ReactPriorityLevel'), + NoWork = _require3.NoWork, + OffscreenPriority = _require3.OffscreenPriority; + +var _require4 = require('./ReactFiberUpdateQueue'), + createUpdateQueue = _require4.createUpdateQueue, + addToQueue = _require4.addToQueue, + addCallbackToQueue = _require4.addCallbackToQueue, + mergeUpdateQueue = _require4.mergeUpdateQueue; + +var ReactInstanceMap = require('./ReactInstanceMap'); + +module.exports = function (config, getScheduler) { + + function markChildAsProgressed(current, workInProgress, priorityLevel) { + // We now have clones. Let's store them as the currently progressed work. + workInProgress.progressedChild = workInProgress.child; + workInProgress.progressedPriority = priorityLevel; + if (current) { + // We also store it on the current. When the alternate swaps in we can + // continue from this point. + current.progressedChild = workInProgress.progressedChild; + current.progressedPriority = workInProgress.progressedPriority; + } + } + + function reconcileChildren(current, workInProgress, nextChildren) { + var priorityLevel = workInProgress.pendingWorkPriority; + reconcileChildrenAtPriority(current, workInProgress, nextChildren, priorityLevel); + } + + function reconcileChildrenAtPriority(current, workInProgress, nextChildren, priorityLevel) { + // At this point any memoization is no longer valid since we'll have changed + // the children. + workInProgress.memoizedProps = null; + if (current && current.child === workInProgress.child) { + // If the current child is the same as the work in progress, it means that + // we haven't yet started any work on these children. Therefore, we use + // the clone algorithm to create a copy of all the current children. + workInProgress.child = reconcileChildFibers(workInProgress, workInProgress.child, nextChildren, priorityLevel); + } else { + // If, on the other hand, we don't have a current fiber or if it is + // already using a clone, that means we've already begun some work on this + // tree and we can continue where we left off by reconciling against the + // existing children. + workInProgress.child = reconcileChildFibersInPlace(workInProgress, workInProgress.child, nextChildren, priorityLevel); + } + markChildAsProgressed(current, workInProgress, priorityLevel); + } + + function updateFunctionalComponent(current, workInProgress) { + var fn = workInProgress.type; + var props = workInProgress.pendingProps; + + // TODO: Disable this before release, since it is not part of the public API + // I use this for testing to compare the relative overhead of classes. + if (typeof fn.shouldComponentUpdate === 'function') { + if (workInProgress.memoizedProps !== null) { + if (!fn.shouldComponentUpdate(workInProgress.memoizedProps, props)) { + return bailoutOnAlreadyFinishedWork(current, workInProgress); + } + } + } + + var nextChildren = fn(props); + reconcileChildren(current, workInProgress, nextChildren); + return workInProgress.child; + } + + function scheduleUpdate(fiber, updateQueue, priorityLevel) { + var _getScheduler = getScheduler(), + scheduleDeferredWork = _getScheduler.scheduleDeferredWork; + + fiber.updateQueue = updateQueue; + // Schedule update on the alternate as well, since we don't know which tree + // is current. + if (fiber.alternate) { + fiber.alternate.updateQueue = updateQueue; + } + while (true) { + if (fiber.pendingWorkPriority === NoWork || fiber.pendingWorkPriority >= priorityLevel) { + fiber.pendingWorkPriority = priorityLevel; + } + if (fiber.alternate) { + if (fiber.alternate.pendingWorkPriority === NoWork || fiber.alternate.pendingWorkPriority >= priorityLevel) { + fiber.alternate.pendingWorkPriority = priorityLevel; + } + } + // Duck type root + if (fiber.stateNode && fiber.stateNode.containerInfo) { + var root = fiber.stateNode; + scheduleDeferredWork(root, priorityLevel); + return; + } + if (!fiber['return']) { + throw new Error('No root!'); + } + fiber = fiber['return']; + } + } + + // Class component state updater + var updater = { + enqueueSetState: function (instance, partialState) { + var fiber = ReactInstanceMap.get(instance); + var updateQueue = fiber.updateQueue ? addToQueue(fiber.updateQueue, partialState) : createUpdateQueue(partialState); + scheduleUpdate(fiber, updateQueue, LowPriority); + }, + enqueueReplaceState: function (instance, state) { + var fiber = ReactInstanceMap.get(instance); + var updateQueue = createUpdateQueue(state); + updateQueue.isReplace = true; + scheduleUpdate(fiber, updateQueue, LowPriority); + }, + enqueueForceUpdate: function (instance) { + var fiber = ReactInstanceMap.get(instance); + var updateQueue = fiber.updateQueue || createUpdateQueue(null); + updateQueue.isForced = true; + scheduleUpdate(fiber, updateQueue, LowPriority); + }, + enqueueCallback: function (instance, callback) { + var fiber = ReactInstanceMap.get(instance); + var updateQueue = fiber.updateQueue ? fiber.updateQueue : createUpdateQueue(null); + addCallbackToQueue(updateQueue, callback); + fiber.updateQueue = updateQueue; + if (fiber.alternate) { + fiber.alternate.updateQueue = updateQueue; + } + } + }; + + function updateClassComponent(current, workInProgress) { + // A class component update is the result of either new props or new state. + // Account for the possibly of missing pending props by falling back to the + // memoized props. + var props = workInProgress.pendingProps; + if (!props && current) { + props = current.memoizedProps; + } + // Compute the state using the memoized state and the update queue. + var updateQueue = workInProgress.updateQueue; + var previousState = current ? current.memoizedState : null; + var state = updateQueue ? mergeUpdateQueue(updateQueue, previousState, props) : previousState; + + var instance = workInProgress.stateNode; + if (!instance) { + var ctor = workInProgress.type; + workInProgress.stateNode = instance = new ctor(props); + state = instance.state || null; + // The initial state must be added to the update queue in case + // setState is called before the initial render. + if (state !== null) { + workInProgress.updateQueue = createUpdateQueue(state); + } + // The instance needs access to the fiber so that it can schedule updates + ReactInstanceMap.set(instance, workInProgress); + instance.updater = updater; + } else if (typeof instance.shouldComponentUpdate === 'function' && !(updateQueue && updateQueue.isForced)) { + if (workInProgress.memoizedProps !== null) { + // Reset the props, in case this is a ping-pong case rather than a + // completed update case. For the completed update case, the instance + // props will already be the memoizedProps. + instance.props = workInProgress.memoizedProps; + instance.state = workInProgress.memoizedState; + if (!instance.shouldComponentUpdate(props, state)) { + return bailoutOnAlreadyFinishedWork(current, workInProgress); + } + } + } + + instance.props = props; + instance.state = state; + var nextChildren = instance.render(); + reconcileChildren(current, workInProgress, nextChildren); + + return workInProgress.child; + } + + function updateHostComponent(current, workInProgress) { + var nextChildren = workInProgress.pendingProps.children; + if (workInProgress.pendingProps.hidden && workInProgress.pendingWorkPriority !== OffscreenPriority) { + // If this host component is hidden, we can bail out on the children. + // We'll rerender the children later at the lower priority. + + // It is unfortunate that we have to do the reconciliation of these + // children already since that will add them to the tree even though + // they are not actually done yet. If this is a large set it is also + // confusing that this takes time to do right now instead of later. + + if (workInProgress.progressedPriority === OffscreenPriority) { + // If we already made some progress on the offscreen priority before, + // then we should continue from where we left off. + workInProgress.child = workInProgress.progressedChild; + } + + // Reconcile the children and stash them for later work. + reconcileChildrenAtPriority(current, workInProgress, nextChildren, OffscreenPriority); + workInProgress.child = current ? current.child : null; + // Abort and don't process children yet. + return null; + } else { + reconcileChildren(current, workInProgress, nextChildren); + return workInProgress.child; + } + } + + function mountIndeterminateComponent(current, workInProgress) { + var fn = workInProgress.type; + var props = workInProgress.pendingProps; + var value = fn(props); + if (typeof value === 'object' && value && typeof value.render === 'function') { + // Proceed under the assumption that this is a class instance + workInProgress.tag = ClassComponent; + if (current) { + current.tag = ClassComponent; + } + value = value.render(); + } else { + // Proceed under the assumption that this is a functional component + workInProgress.tag = FunctionalComponent; + if (current) { + current.tag = FunctionalComponent; + } + } + reconcileChildren(current, workInProgress, value); + return workInProgress.child; + } + + function updateCoroutineComponent(current, workInProgress) { + var coroutine = workInProgress.pendingProps; + if (!coroutine) { + throw new Error('Should be resolved by now'); + } + reconcileChildren(current, workInProgress, coroutine.children); + } + + /* + function reuseChildrenEffects(returnFiber : Fiber, firstChild : Fiber) { + let child = firstChild; + do { + // Ensure that the first and last effect of the parent corresponds + // to the children's first and last effect. + if (!returnFiber.firstEffect) { + returnFiber.firstEffect = child.firstEffect; + } + if (child.lastEffect) { + if (returnFiber.lastEffect) { + returnFiber.lastEffect.nextEffect = child.firstEffect; + } + returnFiber.lastEffect = child.lastEffect; + } + } while (child = child.sibling); + } + */ + + function bailoutOnAlreadyFinishedWork(current, workInProgress) { + var priorityLevel = workInProgress.pendingWorkPriority; + + // TODO: We should ideally be able to bail out early if the children have no + // more work to do. However, since we don't have a separation of this + // Fiber's priority and its children yet - we don't know without doing lots + // of the same work we do anyway. Once we have that separation we can just + // bail out here if the children has no more work at this priority level. + // if (workInProgress.priorityOfChildren <= priorityLevel) { + // // If there are side-effects in these children that have not yet been + // // committed we need to ensure that they get properly transferred up. + // if (current && current.child !== workInProgress.child) { + // reuseChildrenEffects(workInProgress, child); + // } + // return null; + // } + + cloneChildFibers(current, workInProgress); + markChildAsProgressed(current, workInProgress, priorityLevel); + return workInProgress.child; + } + + function bailoutOnLowPriority(current, workInProgress) { + if (current) { + workInProgress.child = current.child; + workInProgress.memoizedProps = current.memoizedProps; + workInProgress.output = current.output; + } + return null; + } + + function beginWork(current, workInProgress, priorityLevel) { + if (workInProgress.pendingWorkPriority === NoWork || workInProgress.pendingWorkPriority > priorityLevel) { + return bailoutOnLowPriority(current, workInProgress); + } + + if (workInProgress.progressedPriority === priorityLevel) { + // If we have progressed work on this priority level already, we can + // proceed this that as the child. + workInProgress.child = workInProgress.progressedChild; + } + + if ((workInProgress.pendingProps === null || workInProgress.memoizedProps !== null && workInProgress.pendingProps === workInProgress.memoizedProps) && workInProgress.updateQueue === null) { + return bailoutOnAlreadyFinishedWork(current, workInProgress); + } + + switch (workInProgress.tag) { + case IndeterminateComponent: + return mountIndeterminateComponent(current, workInProgress); + case FunctionalComponent: + return updateFunctionalComponent(current, workInProgress); + case ClassComponent: + return updateClassComponent(current, workInProgress); + case HostContainer: + reconcileChildren(current, workInProgress, workInProgress.pendingProps); + // A yield component is just a placeholder, we can just run through the + // next one immediately. + if (workInProgress.child) { + return beginWork(workInProgress.child.alternate, workInProgress.child, priorityLevel); + } + return null; + case HostComponent: + if (workInProgress.stateNode && typeof config.beginUpdate === 'function') { + config.beginUpdate(workInProgress.stateNode); + } + return updateHostComponent(current, workInProgress); + case CoroutineHandlerPhase: + // This is a restart. Reset the tag to the initial phase. + workInProgress.tag = CoroutineComponent; + // Intentionally fall through since this is now the same. + case CoroutineComponent: + updateCoroutineComponent(current, workInProgress); + // This doesn't take arbitrary time so we could synchronously just begin + // eagerly do the work of workInProgress.child as an optimization. + if (workInProgress.child) { + return beginWork(workInProgress.child.alternate, workInProgress.child, priorityLevel); + } + return workInProgress.child; + case YieldComponent: + // A yield component is just a placeholder, we can just run through the + // next one immediately. + if (workInProgress.sibling) { + return beginWork(workInProgress.sibling.alternate, workInProgress.sibling, priorityLevel); + } + return null; + default: + throw new Error('Unknown unit of work tag'); + } + } + + return { + beginWork: beginWork + }; +};
\ No newline at end of file |