diff --git a/src/renderers/shared/fiber/ReactFiber.js b/src/renderers/shared/fiber/ReactFiber.js index 6c1d557325aaa..0eee40f1b31ce 100644 --- a/src/renderers/shared/fiber/ReactFiber.js +++ b/src/renderers/shared/fiber/ReactFiber.js @@ -253,6 +253,7 @@ exports.cloneFiber = function(fiber : Fiber, priorityLevel : PriorityLevel) : Fi alt.pendingWorkPriority = priorityLevel; alt.memoizedProps = fiber.memoizedProps; + alt.memoizedState = fiber.memoizedState; alt.output = fiber.output; return alt; diff --git a/src/renderers/shared/fiber/ReactFiberBeginWork.js b/src/renderers/shared/fiber/ReactFiberBeginWork.js index c03d7097382e3..b9bac151de44d 100644 --- a/src/renderers/shared/fiber/ReactFiberBeginWork.js +++ b/src/renderers/shared/fiber/ReactFiberBeginWork.js @@ -14,11 +14,8 @@ import type { ReactCoroutine } from 'ReactCoroutine'; import type { Fiber } from 'ReactFiber'; -import type { FiberRoot } from 'ReactFiberRoot'; import type { HostConfig } from 'ReactFiberReconciler'; -import type { Scheduler } from 'ReactFiberScheduler'; import type { PriorityLevel } from 'ReactPriorityLevel'; -import type { UpdateQueue } from 'ReactFiberUpdateQueue'; var { mountChildFibersInPlace, @@ -26,7 +23,6 @@ var { reconcileChildFibersInPlace, cloneChildFibers, } = require('ReactChildFiber'); -var { LowPriority } = require('ReactPriorityLevel'); var ReactTypeOfWork = require('ReactTypeOfWork'); var { IndeterminateComponent, @@ -45,17 +41,18 @@ var { OffscreenPriority, } = require('ReactPriorityLevel'); var { - createUpdateQueue, - addToQueue, - addCallbackToQueue, mergeUpdateQueue, } = require('ReactFiberUpdateQueue'); var { Placement, } = require('ReactTypeOfSideEffect'); -var ReactInstanceMap = require('ReactInstanceMap'); +var ReactFiberClassComponent = require('ReactFiberClassComponent'); -module.exports = function(config : HostConfig, getScheduler : () => Scheduler) { +module.exports = function(config : HostConfig, scheduleUpdate : (fiber: Fiber, priorityLevel : PriorityLevel) => void) { + + const { + mount, + } = ReactFiberClassComponent(scheduleUpdate); function markChildAsProgressed(current, workInProgress, priorityLevel) { // We now have clones. Let's store them as the currently progressed work. @@ -158,72 +155,6 @@ module.exports = function(config : HostConfig, g return workInProgress.child; } - function scheduleUpdate(fiber: Fiber, updateQueue: UpdateQueue, priorityLevel : PriorityLevel): void { - const { scheduleDeferredWork } = getScheduler(); - 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) { - const root : FiberRoot = (fiber.stateNode : any); - scheduleDeferredWork(root, priorityLevel); - return; - } - if (!fiber.return) { - throw new Error('No root!'); - } - fiber = fiber.return; - } - } - - // Class component state updater - const updater = { - enqueueSetState(instance, partialState) { - const fiber = ReactInstanceMap.get(instance); - const updateQueue = fiber.updateQueue ? - addToQueue(fiber.updateQueue, partialState) : - createUpdateQueue(partialState); - scheduleUpdate(fiber, updateQueue, LowPriority); - }, - enqueueReplaceState(instance, state) { - const fiber = ReactInstanceMap.get(instance); - const updateQueue = createUpdateQueue(state); - updateQueue.isReplace = true; - scheduleUpdate(fiber, updateQueue, LowPriority); - }, - enqueueForceUpdate(instance) { - const fiber = ReactInstanceMap.get(instance); - const updateQueue = fiber.updateQueue || createUpdateQueue(null); - updateQueue.isForced = true; - scheduleUpdate(fiber, updateQueue, LowPriority); - }, - enqueueCallback(instance, callback) { - const fiber = ReactInstanceMap.get(instance); - let updateQueue = fiber.updateQueue ? - fiber.updateQueue : - createUpdateQueue(null); - addCallbackToQueue(updateQueue, callback); - fiber.updateQueue = updateQueue; - if (fiber.alternate) { - fiber.alternate.updateQueue = updateQueue; - } - }, - }; - function updateClassComponent(current : ?Fiber, workInProgress : Fiber) { // 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 @@ -243,15 +174,8 @@ module.exports = function(config : HostConfig, g if (!instance) { var ctor = workInProgress.type; workInProgress.stateNode = instance = new ctor(props); + mount(workInProgress, instance); 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) { diff --git a/src/renderers/shared/fiber/ReactFiberClassComponent.js b/src/renderers/shared/fiber/ReactFiberClassComponent.js new file mode 100644 index 0000000000000..6110057607cb7 --- /dev/null +++ b/src/renderers/shared/fiber/ReactFiberClassComponent.js @@ -0,0 +1,89 @@ +/** + * 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. + * + * @providesModule ReactFiberClassComponent + * @flow + */ + +'use strict'; + +import type { Fiber } from 'ReactFiber'; +import type { PriorityLevel } from 'ReactPriorityLevel'; +import type { UpdateQueue } from 'ReactFiberUpdateQueue'; + +var { LowPriority } = require('ReactPriorityLevel'); +var { + createUpdateQueue, + addToQueue, + addCallbackToQueue, +} = require('ReactFiberUpdateQueue'); +var ReactInstanceMap = require('ReactInstanceMap'); + +module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : PriorityLevel) => void) { + + function scheduleUpdateQueue(fiber: Fiber, updateQueue: UpdateQueue, priorityLevel : PriorityLevel) { + 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; + } + scheduleUpdate(fiber, priorityLevel); + } + + // Class component state updater + const updater = { + enqueueSetState(instance, partialState) { + const fiber = ReactInstanceMap.get(instance); + const updateQueue = fiber.updateQueue ? + addToQueue(fiber.updateQueue, partialState) : + createUpdateQueue(partialState); + scheduleUpdateQueue(fiber, updateQueue, LowPriority); + }, + enqueueReplaceState(instance, state) { + const fiber = ReactInstanceMap.get(instance); + const updateQueue = createUpdateQueue(state); + updateQueue.isReplace = true; + scheduleUpdateQueue(fiber, updateQueue, LowPriority); + }, + enqueueForceUpdate(instance) { + const fiber = ReactInstanceMap.get(instance); + const updateQueue = fiber.updateQueue || createUpdateQueue(null); + updateQueue.isForced = true; + scheduleUpdateQueue(fiber, updateQueue, LowPriority); + }, + enqueueCallback(instance, callback) { + const fiber = ReactInstanceMap.get(instance); + let updateQueue = fiber.updateQueue ? + fiber.updateQueue : + createUpdateQueue(null); + addCallbackToQueue(updateQueue, callback); + fiber.updateQueue = updateQueue; + if (fiber.alternate) { + fiber.alternate.updateQueue = updateQueue; + } + }, + }; + + function mount(workInProgress : Fiber, instance : any) { + const 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; + } + + return { + mount, + }; + +}; diff --git a/src/renderers/shared/fiber/ReactFiberCommitWork.js b/src/renderers/shared/fiber/ReactFiberCommitWork.js index 837f12d00c22e..a972dc56230f9 100644 --- a/src/renderers/shared/fiber/ReactFiberCommitWork.js +++ b/src/renderers/shared/fiber/ReactFiberCommitWork.js @@ -289,9 +289,7 @@ module.exports = function(config : HostConfig) { } else { if (typeof instance.componentDidUpdate === 'function') { const prevProps = current.memoizedProps; - // TODO: This is the new state. We don't currently have the previous - // state anymore. - const prevState = instance.state || null; + const prevState = current.memoizedState; instance.componentDidUpdate(prevProps, prevState); } } diff --git a/src/renderers/shared/fiber/ReactFiberScheduler.js b/src/renderers/shared/fiber/ReactFiberScheduler.js index a1e0382ef77df..99c8682a17ebe 100644 --- a/src/renderers/shared/fiber/ReactFiberScheduler.js +++ b/src/renderers/shared/fiber/ReactFiberScheduler.js @@ -38,21 +38,17 @@ var { Deletion, } = require('ReactTypeOfSideEffect'); -var timeHeuristicForUnitOfWork = 1; +var { + HostContainer, +} = require('ReactTypeOfWork'); -export type Scheduler = { - scheduleDeferredWork: (root : FiberRoot, priority : PriorityLevel) => void -}; +var timeHeuristicForUnitOfWork = 1; module.exports = function(config : HostConfig) { // Use a closure to circumvent the circular dependency between the scheduler // and ReactFiberBeginWork. Don't know if there's a better way to do this. - let scheduler; - function getScheduler(): Scheduler { - return scheduler; - } - const { beginWork } = ReactFiberBeginWork(config, getScheduler); + const { beginWork } = ReactFiberBeginWork(config, scheduleUpdate); const { completeWork } = ReactFiberCompleteWork(config); const { commitInsertion, commitDeletion, commitWork, commitLifeCycles } = ReactFiberCommitWork(config); @@ -395,6 +391,31 @@ module.exports = function(config : HostConfig) { scheduleAnimationWork(root, defaultPriority); } + function scheduleUpdate(fiber: Fiber, priorityLevel : PriorityLevel): void { + 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; + } + } + if (!fiber.return) { + if (fiber.tag === HostContainer) { + const root : FiberRoot = (fiber.stateNode : any); + scheduleDeferredWork(root, priorityLevel); + return; + } else { + throw new Error('Invalid root'); + } + } + fiber = fiber.return; + } + } + function performWithPriority(priorityLevel : PriorityLevel, fn : Function) { const previousDefaultPriority = defaultPriority; defaultPriority = priorityLevel; @@ -405,10 +426,9 @@ module.exports = function(config : HostConfig) { } } - scheduler = { + return { scheduleWork: scheduleWork, scheduleDeferredWork: scheduleDeferredWork, performWithPriority: performWithPriority, }; - return scheduler; }; diff --git a/src/renderers/shared/fiber/ReactFiberUpdateQueue.js b/src/renderers/shared/fiber/ReactFiberUpdateQueue.js index aab252b655846..d352dd82c5f60 100644 --- a/src/renderers/shared/fiber/ReactFiberUpdateQueue.js +++ b/src/renderers/shared/fiber/ReactFiberUpdateQueue.js @@ -39,7 +39,7 @@ exports.createUpdateQueue = function(partialState : mixed) : UpdateQueue { return queue; }; -exports.addToQueue = function(queue : UpdateQueue, partialState : mixed) : UpdateQueue { +function addToQueue(queue : UpdateQueue, partialState : mixed) : UpdateQueue { const node = { partialState, callback: null, @@ -49,12 +49,14 @@ exports.addToQueue = function(queue : UpdateQueue, partialState : mixed) : Updat queue.tail.next = node; queue.tail = node; return queue; -}; +} + +exports.addToQueue = addToQueue; exports.addCallbackToQueue = function(queue : UpdateQueue, callback: Function) : UpdateQueue { if (queue.tail.callback) { // If the tail already as a callback, add an empty node to queue - exports.addToQueue(queue, null); + addToQueue(queue, null); } queue.tail.callback = callback; return queue;