main
js 146 lines 4.79 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 import ReactNoopUpdateQueue from './ReactNoopUpdateQueue';
9 import assign from 'shared/assign';
10
11 const emptyObject = {};
12 if (__DEV__) {
13 Object.freeze(emptyObject);
14 }
15
16 /**
17 * Base class helpers for the updating state of a component.
18 */
19 function Component(props, context, updater) {
20 this.props = props;
21 this.context = context;
22 // If a component has string refs, we will assign a different object later.
23 this.refs = emptyObject;
24 // We initialize the default updater but the real one gets injected by the
25 // renderer.
26 this.updater = updater || ReactNoopUpdateQueue;
27 }
28
29 Component.prototype.isReactComponent = {};
30
31 /**
32 * Sets a subset of the state. Always use this to mutate
33 * state. You should treat `this.state` as immutable.
34 *
35 * There is no guarantee that `this.state` will be immediately updated, so
36 * accessing `this.state` after calling this method may return the old value.
37 *
38 * There is no guarantee that calls to `setState` will run synchronously,
39 * as they may eventually be batched together. You can provide an optional
40 * callback that will be executed when the call to setState is actually
41 * completed.
42 *
43 * When a function is provided to setState, it will be called at some point in
44 * the future (not synchronously). It will be called with the up to date
45 * component arguments (state, props, context). These values can be different
46 * from this.* because your function may be called after receiveProps but before
47 * shouldComponentUpdate, and this new state, props, and context will not yet be
48 * assigned to this.
49 *
50 * @param {object|function} partialState Next partial state or function to
51 * produce next partial state to be merged with current state.
52 * @param {?function} callback Called after state is updated.
53 * @final
54 * @protected
55 */
56 Component.prototype.setState = function (partialState, callback) {
57 if (
58 typeof partialState !== 'object' &&
59 typeof partialState !== 'function' &&
60 partialState != null
61 ) {
62 throw new Error(
63 'takes an object of state variables to update or a ' +
64 'function which returns an object of state variables.',
65 );
66 }
67
68 this.updater.enqueueSetState(this, partialState, callback, 'setState');
69 };
70
71 /**
72 * Forces an update. This should only be invoked when it is known with
73 * certainty that we are **not** in a DOM transaction.
74 *
75 * You may want to call this when you know that some deeper aspect of the
76 * component's state has changed but `setState` was not called.
77 *
78 * This will not invoke `shouldComponentUpdate`, but it will invoke
79 * `componentWillUpdate` and `componentDidUpdate`.
80 *
81 * @param {?function} callback Called after update is complete.
82 * @final
83 * @protected
84 */
85 Component.prototype.forceUpdate = function (callback) {
86 this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
87 };
88
89 /**
90 * Deprecated APIs. These APIs used to exist on classic React classes but since
91 * we would like to deprecate them, we're not going to move them over to this
92 * modern base class. Instead, we define a getter that warns if it's accessed.
93 */
94 if (__DEV__) {
95 const deprecatedAPIs = {
96 isMounted: [
97 'isMounted',
98 'Instead, make sure to clean up subscriptions and pending requests in ' +
99 'componentWillUnmount to prevent memory leaks.',
100 ],
101 replaceState: [
102 'replaceState',
103 'Refactor your code to use setState instead (see ' +
104 'https://github.com/facebook/react/issues/3236).',
105 ],
106 };
107 const defineDeprecationWarning = function (methodName, info) {
108 Object.defineProperty(Component.prototype, methodName, {
109 get: function () {
110 console.warn(
111 '%s(...) is deprecated in plain JavaScript React classes. %s',
112 info[0],
113 info[1],
114 );
115 return undefined;
116 },
117 });
118 };
119 for (const fnName in deprecatedAPIs) {
120 if (deprecatedAPIs.hasOwnProperty(fnName)) {
121 defineDeprecationWarning(fnName, deprecatedAPIs[fnName]);
122 }
123 }
124 }
125
126 function ComponentDummy() {}
127 ComponentDummy.prototype = Component.prototype;
128
129 /**
130 * Convenience component with default shallow equality check for sCU.
131 */
132 function PureComponent(props, context, updater) {
133 this.props = props;
134 this.context = context;
135 // If a component has string refs, we will assign a different object later.
136 this.refs = emptyObject;
137 this.updater = updater || ReactNoopUpdateQueue;
138 }
139
140 const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
141 pureComponentPrototype.constructor = PureComponent;
142 // Avoid an extra prototype jump for these methods.
143 assign(pureComponentPrototype, Component.prototype);
144 pureComponentPrototype.isPureReactComponent = true;
145
146 export {Component, PureComponent};