-
Notifications
You must be signed in to change notification settings - Fork 48.4k
[Fiber] Add more life-cycles #8015
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ var { | |
createUpdateQueue, | ||
addToQueue, | ||
addCallbackToQueue, | ||
mergeUpdateQueue, | ||
} = require('ReactFiberUpdateQueue'); | ||
var ReactInstanceMap = require('ReactInstanceMap'); | ||
|
||
|
@@ -70,20 +71,155 @@ module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : Priori | |
}, | ||
}; | ||
|
||
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); | ||
} | ||
function adoptClassInstance(workInProgress : Fiber, instance : any) : void { | ||
instance.updater = updater; | ||
workInProgress.stateNode = instance; | ||
// The instance needs access to the fiber so that it can schedule updates | ||
ReactInstanceMap.set(instance, workInProgress); | ||
instance.updater = updater; | ||
} | ||
|
||
function constructClassInstance(workInProgress : Fiber) : any { | ||
const ctor = workInProgress.type; | ||
const props = workInProgress.pendingProps; | ||
const instance = new ctor(props); | ||
adoptClassInstance(workInProgress, instance); | ||
return instance; | ||
} | ||
|
||
// Invokes the mount life-cycles on a previously never rendered instance. | ||
function mountClassInstance(workInProgress : Fiber) : void { | ||
const instance = workInProgress.stateNode; | ||
|
||
const state = instance.state || null; | ||
|
||
// 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. | ||
let props = workInProgress.pendingProps; | ||
if (!props) { | ||
throw new Error('There must be pending props for an initial mount.'); | ||
} | ||
|
||
instance.props = props; | ||
instance.state = state; | ||
|
||
if (typeof instance.componentWillMount === 'function') { | ||
instance.componentWillMount(); | ||
// If we had additional state updates during this life-cycle, let's | ||
// process them now. | ||
const updateQueue = workInProgress.updateQueue; | ||
if (updateQueue) { | ||
instance.state = mergeUpdateQueue(updateQueue, state, props); | ||
} | ||
} | ||
} | ||
|
||
// Called on a preexisting class instance. Returns false if a resumed render | ||
// could be reused. | ||
function resumeMountClassInstance(workInProgress : Fiber) : boolean { | ||
const instance = workInProgress.stateNode; | ||
let newState = workInProgress.memoizedState; | ||
let newProps = workInProgress.pendingProps; | ||
if (!newProps) { | ||
// If there isn't any new props, then we'll reuse the memoized props. | ||
// This could be from already completed work. | ||
newProps = workInProgress.memoizedProps; | ||
if (!newProps) { | ||
throw new Error('There should always be pending or memoized props.'); | ||
} | ||
} | ||
|
||
// TODO: Should we deal with a setState that happened after the last | ||
// componentWillMount and before this componentWillMount? Probably | ||
// unsupported anyway. | ||
|
||
const updateQueue = workInProgress.updateQueue; | ||
|
||
// If this completed, we might be able to just reuse this instance. | ||
if (typeof instance.shouldComponentUpdate === 'function' && | ||
!(updateQueue && updateQueue.isForced) && | ||
workInProgress.memoizedProps !== null && | ||
!instance.shouldComponentUpdate(newProps, newState)) { | ||
return false; | ||
} | ||
|
||
// If we didn't bail out we need to construct a new instance. We don't | ||
// want to reuse one that failed to fully mount. | ||
const newInstance = constructClassInstance(workInProgress); | ||
newInstance.props = newProps; | ||
newInstance.state = newState = newInstance.state || null; | ||
|
||
if (typeof newInstance.componentWillMount === 'function') { | ||
newInstance.componentWillMount(); | ||
// If we had additional state updates during this life-cycle, let's | ||
// process them now. | ||
const newUpdateQueue = workInProgress.updateQueue; | ||
if (newUpdateQueue) { | ||
instance.state = mergeUpdateQueue(newUpdateQueue, newState, newProps); | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
// Invokes the update life-cycles and returns false if it shouldn't rerender. | ||
function updateClassInstance(current : Fiber, workInProgress : Fiber) : boolean { | ||
const instance = workInProgress.stateNode; | ||
|
||
const oldProps = current.memoizedProps; | ||
let newProps = workInProgress.pendingProps; | ||
if (!newProps) { | ||
// If there aren't any new props, then we'll reuse the memoized props. | ||
// This could be from already completed work. | ||
newProps = workInProgress.memoizedProps; | ||
if (!newProps) { | ||
throw new Error('There should always be pending or memoized props.'); | ||
} | ||
} | ||
|
||
// Note: During these life-cycles, instance.props/instance.state are what | ||
// ever the previously attempted to render - not the "current". However, | ||
// during componentDidUpdate we pass the "current" props. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the intended behavior? I wonder if this breaks any user assumptions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like discussed in chat, this is intended. It's a bit confusing but it's the most likely semantics that will work with current code. The primary goal is to have a nicer upgrade path from existing code. Ease of understanding the semantics of the life-cycles can be achieved by redesigning a new API for them. |
||
|
||
if (oldProps !== newProps) { | ||
if (typeof instance.componentWillReceiveProps === 'function') { | ||
instance.componentWillReceiveProps(newProps); | ||
} | ||
} | ||
|
||
// Compute the next state using the memoized state and the update queue. | ||
const updateQueue = workInProgress.updateQueue; | ||
const previousState = workInProgress.memoizedState; | ||
// TODO: Previous state can be null. | ||
let newState; | ||
if (updateQueue) { | ||
newState = mergeUpdateQueue(updateQueue, previousState, newProps); | ||
} else { | ||
newState = previousState; | ||
} | ||
|
||
if (typeof instance.shouldComponentUpdate === 'function' && | ||
!(updateQueue && updateQueue.isForced) && | ||
workInProgress.memoizedProps !== null && | ||
!instance.shouldComponentUpdate(newProps, newState)) { | ||
// TODO: Should this get the new props/state updated regardless? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean whether we should reassign |
||
return false; | ||
} | ||
|
||
if (typeof instance.componentWillUpdate === 'function') { | ||
instance.componentWillUpdate(newProps, newState); | ||
} | ||
|
||
instance.props = newProps; | ||
instance.state = newState; | ||
return true; | ||
} | ||
|
||
return { | ||
mount, | ||
adoptClassInstance, | ||
constructClassInstance, | ||
mountClassInstance, | ||
resumeMountClassInstance, | ||
updateClassInstance, | ||
}; | ||
|
||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this comment referring to? You throw on missing
pendingProps
below. Copypaste?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea. This is copypaste.