-
Notifications
You must be signed in to change notification settings - Fork 47.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Trigger a proper no-op warning for async state changes on server (#7127)
This commit fixes #5473: ReactDOMServer.renderToString: presence of onClick handler causes errors on async update This commit performs the following changes: - Adds a getUpdateQueue method to ReactServerRenderingTransaction, ReactReconcileTransaction, ReactNativeReconcileTransaction and ReactTestReconcileTransaction - Make the ReactCompositeComponent call this getUpdateQueue instead of using ReactUpdateQueue that was unwanted at certain moments on server - On ReactServerRenderingTransaction, dispatch ReactUpdateQueue's methods while rendering and warning methods afterwards. This is done through the new ReactServerUpdateQueue class - Added a series of tests that mimics the case presented in #5473 with setState, forceUpdate and replaceState - Add flow typechecking on concerned files
- Loading branch information
Showing
8 changed files
with
260 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
/** | ||
* Copyright 2015-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 ReactServerUpdateQueue | ||
* @flow | ||
*/ | ||
|
||
'use strict'; | ||
|
||
var ReactUpdateQueue = require('ReactUpdateQueue'); | ||
var Transaction = require('Transaction'); | ||
var warning = require('warning'); | ||
|
||
function warnNoop(publicInstance: ReactComponent<any, any, any>, callerName: string) { | ||
if (__DEV__) { | ||
var constructor = publicInstance.constructor; | ||
warning( | ||
false, | ||
'%s(...): Can only update a mounting component. ' + | ||
'This usually means you called %s() outside componentWillMount() on the server. ' + | ||
'This is a no-op. Please check the code for the %s component.', | ||
callerName, | ||
callerName, | ||
constructor && (constructor.displayName || constructor.name) || 'ReactClass' | ||
); | ||
} | ||
} | ||
|
||
/** | ||
* This is the update queue used for server rendering. | ||
* It delegates to ReactUpdateQueue while server rendering is in progress and | ||
* switches to ReactNoopUpdateQueue after the transaction has completed. | ||
* @class ReactServerUpdateQueue | ||
* @param {Transaction} transaction | ||
*/ | ||
class ReactServerUpdateQueue { | ||
/* :: transaction: Transaction; */ | ||
|
||
constructor(transaction: Transaction) { | ||
this.transaction = transaction; | ||
} | ||
|
||
/** | ||
* Checks whether or not this composite component is mounted. | ||
* @param {ReactClass} publicInstance The instance we want to test. | ||
* @return {boolean} True if mounted, false otherwise. | ||
* @protected | ||
* @final | ||
*/ | ||
isMounted(publicInstance: ReactComponent<any, any, any>): boolean { | ||
return false; | ||
} | ||
|
||
/** | ||
* Enqueue a callback that will be executed after all the pending updates | ||
* have processed. | ||
* | ||
* @param {ReactClass} publicInstance The instance to use as `this` context. | ||
* @param {?function} callback Called after state is updated. | ||
* @internal | ||
*/ | ||
enqueueCallback(publicInstance: ReactComponent<any, any, any>, callback?: Function, callerName?: string) { | ||
if (this.transaction.isInTransaction()) { | ||
ReactUpdateQueue.enqueueCallback(publicInstance, callback, callerName); | ||
} | ||
} | ||
|
||
/** | ||
* Forces an update. This should only be invoked when it is known with | ||
* certainty that we are **not** in a DOM transaction. | ||
* | ||
* You may want to call this when you know that some deeper aspect of the | ||
* component's state has changed but `setState` was not called. | ||
* | ||
* This will not invoke `shouldComponentUpdate`, but it will invoke | ||
* `componentWillUpdate` and `componentDidUpdate`. | ||
* | ||
* @param {ReactClass} publicInstance The instance that should rerender. | ||
* @internal | ||
*/ | ||
enqueueForceUpdate(publicInstance: ReactComponent<any, any, any>) { | ||
if (this.transaction.isInTransaction()) { | ||
ReactUpdateQueue.enqueueForceUpdate(publicInstance); | ||
} else { | ||
warnNoop(publicInstance, 'forceUpdate'); | ||
} | ||
} | ||
|
||
/** | ||
* Replaces all of the state. Always use this or `setState` to mutate state. | ||
* You should treat `this.state` as immutable. | ||
* | ||
* There is no guarantee that `this.state` will be immediately updated, so | ||
* accessing `this.state` after calling this method may return the old value. | ||
* | ||
* @param {ReactClass} publicInstance The instance that should rerender. | ||
* @param {object|function} completeState Next state. | ||
* @internal | ||
*/ | ||
enqueueReplaceState(publicInstance: ReactComponent<any, any, any>, completeState: Object|Function) { | ||
if (this.transaction.isInTransaction()) { | ||
ReactUpdateQueue.enqueueReplaceState(publicInstance, completeState); | ||
} else { | ||
warnNoop(publicInstance, 'replaceState'); | ||
} | ||
} | ||
|
||
/** | ||
* Sets a subset of the state. This only exists because _pendingState is | ||
* internal. This provides a merging strategy that is not available to deep | ||
* properties which is confusing. TODO: Expose pendingState or don't use it | ||
* during the merge. | ||
* | ||
* @param {ReactClass} publicInstance The instance that should rerender. | ||
* @param {object|function} partialState Next partial state to be merged with state. | ||
* @internal | ||
*/ | ||
enqueueSetState(publicInstance: ReactComponent<any, any, any>, partialState: Object|Function) { | ||
if (this.transaction.isInTransaction()) { | ||
ReactUpdateQueue.enqueueSetState(publicInstance, partialState); | ||
} else { | ||
warnNoop(publicInstance, 'setState'); | ||
} | ||
} | ||
} | ||
|
||
module.exports = ReactServerUpdateQueue; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.