-
Notifications
You must be signed in to change notification settings - Fork 47.3k
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
Support class component static contextType attribute #13728
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 |
---|---|---|
|
@@ -65,6 +65,8 @@ let didWarnAboutUndefinedDerivedState; | |
let warnOnUndefinedDerivedState; | ||
let warnOnInvalidCallback; | ||
let didWarnAboutDirectlyAssigningPropsToState; | ||
let didWarnAboutContextTypeAndContextTypes; | ||
let didWarnAboutInvalidateContextType; | ||
|
||
if (__DEV__) { | ||
didWarnAboutStateAssignmentForComponent = new Set(); | ||
|
@@ -73,6 +75,8 @@ if (__DEV__) { | |
didWarnAboutLegacyLifecyclesAndDerivedState = new Set(); | ||
didWarnAboutDirectlyAssigningPropsToState = new Set(); | ||
didWarnAboutUndefinedDerivedState = new Set(); | ||
didWarnAboutContextTypeAndContextTypes = new Set(); | ||
didWarnAboutInvalidateContextType = new Set(); | ||
|
||
const didWarnOnInvalidCallback = new Set(); | ||
|
||
|
@@ -234,15 +238,15 @@ function checkShouldComponentUpdate( | |
newProps, | ||
oldState, | ||
newState, | ||
nextLegacyContext, | ||
nextContext, | ||
) { | ||
const instance = workInProgress.stateNode; | ||
if (typeof instance.shouldComponentUpdate === 'function') { | ||
startPhaseTimer(workInProgress, 'shouldComponentUpdate'); | ||
const shouldUpdate = instance.shouldComponentUpdate( | ||
newProps, | ||
newState, | ||
nextLegacyContext, | ||
nextContext, | ||
); | ||
stopPhaseTimer(); | ||
|
||
|
@@ -319,13 +323,50 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { | |
'property to define propTypes instead.', | ||
name, | ||
); | ||
const noInstanceContextType = !instance.contextType; | ||
warningWithoutStack( | ||
noInstanceContextType, | ||
'contextType was defined as an instance property on %s. Use a static ' + | ||
'property to define contextType instead.', | ||
name, | ||
); | ||
const noInstanceContextTypes = !instance.contextTypes; | ||
warningWithoutStack( | ||
noInstanceContextTypes, | ||
'contextTypes was defined as an instance property on %s. Use a static ' + | ||
'property to define contextTypes instead.', | ||
name, | ||
); | ||
|
||
if ( | ||
ctor.contextType && | ||
ctor.contextTypes && | ||
!didWarnAboutContextTypeAndContextTypes.has(ctor) | ||
) { | ||
didWarnAboutContextTypeAndContextTypes.add(ctor); | ||
warningWithoutStack( | ||
false, | ||
'%s declares both contextTypes and contextType static properties. ' + | ||
'The legacy contextTypes property will be ignored.', | ||
name, | ||
); | ||
} | ||
|
||
if ( | ||
ctor.contextType && | ||
typeof ctor.contextType.unstable_read !== 'function' && | ||
!didWarnAboutInvalidateContextType.has(ctor) | ||
) { | ||
didWarnAboutInvalidateContextType.add(ctor); | ||
warningWithoutStack( | ||
false, | ||
'%s defines an invalid contextType. ' + | ||
'contextType should point to the Context object returned by React.createContext(). ' + | ||
'Did you accidentally pass the Context.Provider instead?', | ||
name, | ||
); | ||
} | ||
|
||
const noComponentShouldUpdate = | ||
typeof instance.componentShouldUpdate !== 'function'; | ||
warningWithoutStack( | ||
|
@@ -475,12 +516,24 @@ function constructClassInstance( | |
props: any, | ||
renderExpirationTime: ExpirationTime, | ||
): any { | ||
const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true); | ||
const contextTypes = ctor.contextTypes; | ||
const isContextConsumer = contextTypes !== null && contextTypes !== undefined; | ||
const context = isContextConsumer | ||
? getMaskedContext(workInProgress, unmaskedContext) | ||
: emptyContextObject; | ||
let isContextConsumer = false; | ||
let unmaskedContext = emptyContextObject; | ||
let context = null; | ||
const contextType = ctor.contextType; | ||
if ( | ||
typeof contextType === 'object' && | ||
contextType !== null && | ||
typeof contextType.unstable_read === 'function' | ||
) { | ||
context = (contextType: any).unstable_read(); | ||
} else { | ||
unmaskedContext = getUnmaskedContext(workInProgress, ctor, true); | ||
const contextTypes = ctor.contextTypes; | ||
isContextConsumer = contextTypes !== null && contextTypes !== undefined; | ||
context = isContextConsumer | ||
? getMaskedContext(workInProgress, unmaskedContext) | ||
: emptyContextObject; | ||
} | ||
|
||
// Instantiate twice to help detect side-effects. | ||
if (__DEV__) { | ||
|
@@ -625,15 +678,15 @@ function callComponentWillReceiveProps( | |
workInProgress, | ||
instance, | ||
newProps, | ||
nextLegacyContext, | ||
nextContext, | ||
) { | ||
const oldState = instance.state; | ||
startPhaseTimer(workInProgress, 'componentWillReceiveProps'); | ||
if (typeof instance.componentWillReceiveProps === 'function') { | ||
instance.componentWillReceiveProps(newProps, nextLegacyContext); | ||
instance.componentWillReceiveProps(newProps, nextContext); | ||
} | ||
if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') { | ||
instance.UNSAFE_componentWillReceiveProps(newProps, nextLegacyContext); | ||
instance.UNSAFE_componentWillReceiveProps(newProps, nextContext); | ||
} | ||
stopPhaseTimer(); | ||
|
||
|
@@ -668,12 +721,21 @@ function mountClassInstance( | |
} | ||
|
||
const instance = workInProgress.stateNode; | ||
const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true); | ||
|
||
instance.props = newProps; | ||
instance.state = workInProgress.memoizedState; | ||
instance.refs = emptyRefsObject; | ||
instance.context = getMaskedContext(workInProgress, unmaskedContext); | ||
|
||
const contextType = ctor.contextType; | ||
if ( | ||
typeof contextType === 'object' && | ||
contextType !== null && | ||
typeof contextType.unstable_read === 'function' | ||
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 we need this extra check? Seems like we can just throw. 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. I guess we don't, since we also provide a nice DEV mode warning if it's not a function. I'll remove this type check. 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.
Disregard. I'll just move the warning earlier. #13736 |
||
) { | ||
instance.context = (contextType: any).unstable_read(); | ||
} else { | ||
const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true); | ||
instance.context = getMaskedContext(workInProgress, unmaskedContext); | ||
} | ||
|
||
if (__DEV__) { | ||
if (instance.state === newProps) { | ||
|
@@ -774,15 +836,22 @@ function resumeMountClassInstance( | |
instance.props = oldProps; | ||
|
||
const oldContext = instance.context; | ||
const nextLegacyUnmaskedContext = getUnmaskedContext( | ||
workInProgress, | ||
ctor, | ||
true, | ||
); | ||
const nextLegacyContext = getMaskedContext( | ||
workInProgress, | ||
nextLegacyUnmaskedContext, | ||
); | ||
const contextType = ctor.contextType; | ||
let nextContext; | ||
if ( | ||
typeof contextType === 'object' && | ||
contextType !== null && | ||
typeof contextType.unstable_read === 'function' | ||
) { | ||
nextContext = (contextType: any).unstable_read(); | ||
} else { | ||
const nextLegacyUnmaskedContext = getUnmaskedContext( | ||
workInProgress, | ||
ctor, | ||
true, | ||
); | ||
nextContext = getMaskedContext(workInProgress, nextLegacyUnmaskedContext); | ||
} | ||
|
||
const getDerivedStateFromProps = ctor.getDerivedStateFromProps; | ||
const hasNewLifecycles = | ||
|
@@ -800,12 +869,12 @@ function resumeMountClassInstance( | |
(typeof instance.UNSAFE_componentWillReceiveProps === 'function' || | ||
typeof instance.componentWillReceiveProps === 'function') | ||
) { | ||
if (oldProps !== newProps || oldContext !== nextLegacyContext) { | ||
if (oldProps !== newProps || oldContext !== nextContext) { | ||
callComponentWillReceiveProps( | ||
workInProgress, | ||
instance, | ||
newProps, | ||
nextLegacyContext, | ||
nextContext, | ||
); | ||
} | ||
} | ||
|
@@ -858,7 +927,7 @@ function resumeMountClassInstance( | |
newProps, | ||
oldState, | ||
newState, | ||
nextLegacyContext, | ||
nextContext, | ||
); | ||
|
||
if (shouldUpdate) { | ||
|
@@ -898,7 +967,7 @@ function resumeMountClassInstance( | |
// if shouldComponentUpdate returns false. | ||
instance.props = newProps; | ||
instance.state = newState; | ||
instance.context = nextLegacyContext; | ||
instance.context = nextContext; | ||
|
||
return shouldUpdate; | ||
} | ||
|
@@ -917,15 +986,18 @@ function updateClassInstance( | |
instance.props = oldProps; | ||
|
||
const oldContext = instance.context; | ||
const nextLegacyUnmaskedContext = getUnmaskedContext( | ||
workInProgress, | ||
ctor, | ||
true, | ||
); | ||
const nextLegacyContext = getMaskedContext( | ||
workInProgress, | ||
nextLegacyUnmaskedContext, | ||
); | ||
const contextType = ctor.contextType; | ||
let nextContext; | ||
if ( | ||
typeof contextType === 'object' && | ||
contextType !== null && | ||
typeof contextType.unstable_read === 'function' | ||
) { | ||
nextContext = (contextType: any).unstable_read(); | ||
} else { | ||
const nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true); | ||
nextContext = getMaskedContext(workInProgress, nextUnmaskedContext); | ||
} | ||
|
||
const getDerivedStateFromProps = ctor.getDerivedStateFromProps; | ||
const hasNewLifecycles = | ||
|
@@ -943,12 +1015,12 @@ function updateClassInstance( | |
(typeof instance.UNSAFE_componentWillReceiveProps === 'function' || | ||
typeof instance.componentWillReceiveProps === 'function') | ||
) { | ||
if (oldProps !== newProps || oldContext !== nextLegacyContext) { | ||
if (oldProps !== newProps || oldContext !== nextContext) { | ||
callComponentWillReceiveProps( | ||
workInProgress, | ||
instance, | ||
newProps, | ||
nextLegacyContext, | ||
nextContext, | ||
); | ||
} | ||
} | ||
|
@@ -1015,7 +1087,7 @@ function updateClassInstance( | |
newProps, | ||
oldState, | ||
newState, | ||
nextLegacyContext, | ||
nextContext, | ||
); | ||
|
||
if (shouldUpdate) { | ||
|
@@ -1028,14 +1100,10 @@ function updateClassInstance( | |
) { | ||
startPhaseTimer(workInProgress, 'componentWillUpdate'); | ||
if (typeof instance.componentWillUpdate === 'function') { | ||
instance.componentWillUpdate(newProps, newState, nextLegacyContext); | ||
instance.componentWillUpdate(newProps, newState, nextContext); | ||
} | ||
if (typeof instance.UNSAFE_componentWillUpdate === 'function') { | ||
instance.UNSAFE_componentWillUpdate( | ||
newProps, | ||
newState, | ||
nextLegacyContext, | ||
); | ||
instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext); | ||
} | ||
stopPhaseTimer(); | ||
} | ||
|
@@ -1075,7 +1143,7 @@ function updateClassInstance( | |
// if shouldComponentUpdate returns false. | ||
instance.props = newProps; | ||
instance.state = newState; | ||
instance.context = nextLegacyContext; | ||
instance.context = nextContext; | ||
|
||
return shouldUpdate; | ||
} | ||
|
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.
Can we name this
isLegacyContextConsumer
to be more specific?