UNPKG

9.35 kBJavaScriptView Raw
1/**
2 * Copyright 2015-present, Facebook, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
8 *
9 */
10
11'use strict';
12
13var _prodInvariant = require('./reactProdInvariant');
14
15var ReactCurrentOwner = require('react/lib/ReactCurrentOwner');
16var ReactInstanceMap = require('./ReactInstanceMap');
17var ReactInstrumentation = require('./ReactInstrumentation');
18var ReactUpdates = require('./ReactUpdates');
19
20var invariant = require('fbjs/lib/invariant');
21var warning = require('fbjs/lib/warning');
22
23function enqueueUpdate(internalInstance) {
24 ReactUpdates.enqueueUpdate(internalInstance);
25}
26
27function formatUnexpectedArgument(arg) {
28 var type = typeof arg;
29 if (type !== 'object') {
30 return type;
31 }
32 var displayName = arg.constructor && arg.constructor.name || type;
33 var keys = Object.keys(arg);
34 if (keys.length > 0 && keys.length < 20) {
35 return displayName + ' (keys: ' + keys.join(', ') + ')';
36 }
37 return displayName;
38}
39
40function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
41 var internalInstance = ReactInstanceMap.get(publicInstance);
42 if (!internalInstance) {
43 if (process.env.NODE_ENV !== 'production') {
44 var ctor = publicInstance.constructor;
45 // Only warn when we have a callerName. Otherwise we should be silent.
46 // We're probably calling from enqueueCallback. We don't want to warn
47 // there because we already warned for the corresponding lifecycle method.
48 process.env.NODE_ENV !== 'production' ? warning(!callerName, '%s(...): Can only update a mounted or mounting component. ' + 'This usually means you called %s() on an unmounted component. ' + 'This is a no-op. Please check the code for the %s component.', callerName, callerName, ctor && (ctor.displayName || ctor.name) || 'ReactClass') : void 0;
49 }
50 return null;
51 }
52
53 if (process.env.NODE_ENV !== 'production') {
54 process.env.NODE_ENV !== 'production' ? warning(ReactCurrentOwner.current == null, '%s(...): Cannot update during an existing state transition (such as ' + "within `render` or another component's constructor). Render methods " + 'should be a pure function of props and state; constructor ' + 'side-effects are an anti-pattern, but can be moved to ' + '`componentWillMount`.', callerName) : void 0;
55 }
56
57 return internalInstance;
58}
59
60/**
61 * ReactUpdateQueue allows for state updates to be scheduled into a later
62 * reconciliation step.
63 */
64var ReactUpdateQueue = {
65 /**
66 * Checks whether or not this composite component is mounted.
67 * @param {ReactClass} publicInstance The instance we want to test.
68 * @return {boolean} True if mounted, false otherwise.
69 * @protected
70 * @final
71 */
72 isMounted: function (publicInstance) {
73 if (process.env.NODE_ENV !== 'production') {
74 var owner = ReactCurrentOwner.current;
75 if (owner !== null) {
76 process.env.NODE_ENV !== 'production' ? warning(owner._warnedAboutRefsInRender, '%s is accessing isMounted inside its render() function. ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', owner.getName() || 'A component') : void 0;
77 owner._warnedAboutRefsInRender = true;
78 }
79 }
80 var internalInstance = ReactInstanceMap.get(publicInstance);
81 if (internalInstance) {
82 // During componentWillMount and render this will still be null but after
83 // that will always render to something. At least for now. So we can use
84 // this hack.
85 return !!internalInstance._renderedComponent;
86 } else {
87 return false;
88 }
89 },
90
91 /**
92 * Enqueue a callback that will be executed after all the pending updates
93 * have processed.
94 *
95 * @param {ReactClass} publicInstance The instance to use as `this` context.
96 * @param {?function} callback Called after state is updated.
97 * @param {string} callerName Name of the calling function in the public API.
98 * @internal
99 */
100 enqueueCallback: function (publicInstance, callback, callerName) {
101 ReactUpdateQueue.validateCallback(callback, callerName);
102 var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
103
104 // Previously we would throw an error if we didn't have an internal
105 // instance. Since we want to make it a no-op instead, we mirror the same
106 // behavior we have in other enqueue* methods.
107 // We also need to ignore callbacks in componentWillMount. See
108 // enqueueUpdates.
109 if (!internalInstance) {
110 return null;
111 }
112
113 if (internalInstance._pendingCallbacks) {
114 internalInstance._pendingCallbacks.push(callback);
115 } else {
116 internalInstance._pendingCallbacks = [callback];
117 }
118 // TODO: The callback here is ignored when setState is called from
119 // componentWillMount. Either fix it or disallow doing so completely in
120 // favor of getInitialState. Alternatively, we can disallow
121 // componentWillMount during server-side rendering.
122 enqueueUpdate(internalInstance);
123 },
124
125 enqueueCallbackInternal: function (internalInstance, callback) {
126 if (internalInstance._pendingCallbacks) {
127 internalInstance._pendingCallbacks.push(callback);
128 } else {
129 internalInstance._pendingCallbacks = [callback];
130 }
131 enqueueUpdate(internalInstance);
132 },
133
134 /**
135 * Forces an update. This should only be invoked when it is known with
136 * certainty that we are **not** in a DOM transaction.
137 *
138 * You may want to call this when you know that some deeper aspect of the
139 * component's state has changed but `setState` was not called.
140 *
141 * This will not invoke `shouldComponentUpdate`, but it will invoke
142 * `componentWillUpdate` and `componentDidUpdate`.
143 *
144 * @param {ReactClass} publicInstance The instance that should rerender.
145 * @internal
146 */
147 enqueueForceUpdate: function (publicInstance) {
148 var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'forceUpdate');
149
150 if (!internalInstance) {
151 return;
152 }
153
154 internalInstance._pendingForceUpdate = true;
155
156 enqueueUpdate(internalInstance);
157 },
158
159 /**
160 * Replaces all of the state. Always use this or `setState` to mutate state.
161 * You should treat `this.state` as immutable.
162 *
163 * There is no guarantee that `this.state` will be immediately updated, so
164 * accessing `this.state` after calling this method may return the old value.
165 *
166 * @param {ReactClass} publicInstance The instance that should rerender.
167 * @param {object} completeState Next state.
168 * @internal
169 */
170 enqueueReplaceState: function (publicInstance, completeState, callback) {
171 var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'replaceState');
172
173 if (!internalInstance) {
174 return;
175 }
176
177 internalInstance._pendingStateQueue = [completeState];
178 internalInstance._pendingReplaceState = true;
179
180 // Future-proof 15.5
181 if (callback !== undefined && callback !== null) {
182 ReactUpdateQueue.validateCallback(callback, 'replaceState');
183 if (internalInstance._pendingCallbacks) {
184 internalInstance._pendingCallbacks.push(callback);
185 } else {
186 internalInstance._pendingCallbacks = [callback];
187 }
188 }
189
190 enqueueUpdate(internalInstance);
191 },
192
193 /**
194 * Sets a subset of the state. This only exists because _pendingState is
195 * internal. This provides a merging strategy that is not available to deep
196 * properties which is confusing. TODO: Expose pendingState or don't use it
197 * during the merge.
198 *
199 * @param {ReactClass} publicInstance The instance that should rerender.
200 * @param {object} partialState Next partial state to be merged with state.
201 * @internal
202 */
203 enqueueSetState: function (publicInstance, partialState) {
204 if (process.env.NODE_ENV !== 'production') {
205 ReactInstrumentation.debugTool.onSetState();
206 process.env.NODE_ENV !== 'production' ? warning(partialState != null, 'setState(...): You passed an undefined or null state object; ' + 'instead, use forceUpdate().') : void 0;
207 }
208
209 var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
210
211 if (!internalInstance) {
212 return;
213 }
214
215 var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
216 queue.push(partialState);
217
218 enqueueUpdate(internalInstance);
219 },
220
221 enqueueElementInternal: function (internalInstance, nextElement, nextContext) {
222 internalInstance._pendingElement = nextElement;
223 // TODO: introduce _pendingContext instead of setting it directly.
224 internalInstance._context = nextContext;
225 enqueueUpdate(internalInstance);
226 },
227
228 validateCallback: function (callback, callerName) {
229 !(!callback || typeof callback === 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s(...): Expected the last optional `callback` argument to be a function. Instead received: %s.', callerName, formatUnexpectedArgument(callback)) : _prodInvariant('122', callerName, formatUnexpectedArgument(callback)) : void 0;
230 }
231};
232
233module.exports = ReactUpdateQueue;
\No newline at end of file