mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
173d065a9e
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
(cherry picked from commit dbdddf1c82)
133 lines
4.5 KiB
JavaScript
133 lines
4.5 KiB
JavaScript
/**
|
|
* 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;
|