aboutsummaryrefslogtreecommitdiff
path: root/node_modules/react-dom/lib/ReactFiberBeginWork.js
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2017-05-03 15:35:00 +0200
committerFlorian Dold <florian.dold@gmail.com>2017-05-03 15:35:00 +0200
commitde98e0b232509d5f40c135d540a70e415272ff85 (patch)
treea79222a5b58484ab3b80d18efcaaa7ccc4769b33 /node_modules/react-dom/lib/ReactFiberBeginWork.js
parente0c9d480a73fa629c1e4a47d3e721f1d2d345406 (diff)
node_modules
Diffstat (limited to 'node_modules/react-dom/lib/ReactFiberBeginWork.js')
-rw-r--r--node_modules/react-dom/lib/ReactFiberBeginWork.js380
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