├── hooks_works_internally.md ├── simulate_life_cycle.md ├── fiber.md ├── Readme.md ├── scopes.md └── react.js /hooks_works_internally.md: -------------------------------------------------------------------------------- 1 | # Steps 2 | 3 | On render phase 4 | 5 | - Before render start, 6 | - set a global variable pointing to the current dispatcher (for simplicity consider global variable to points to component instance). 7 | - create a list to store hooks state (if not present already) on the component instance. 8 | - create/reset a current pointer to point to specific hook in the list. 9 | - On render call for every hook 10 | - if there is item in the hook list for the current pointer return that. 11 | - else create a hooks state object, based on type of hook and add the state object to the list. 12 | - if it is effect hook add the effect on effect list on that fiber along with the previous cleanup callback. _11594_ 13 | - increment the current pointer for the next hook. 14 | - return the value from the hook (based on the type of hook). 15 | 16 | On commit phase 17 | 18 | - Before commit call all the cleanup effect callback. 19 | - After commit call the effect callback. 20 | -------------------------------------------------------------------------------- /simulate_life_cycle.md: -------------------------------------------------------------------------------- 1 | ### constructor 2 | 3 | ```js 4 | function MyApp() => { 5 | const [state, setState] = useState(() => { 6 | // do one time heavy computation here, which will be done on first render 7 | }) 8 | 9 | const someValue = useMemo(() => { 10 | // do things you want to do one time which will be done on first render 11 | }, []) 12 | } 13 | ``` 14 | 15 | ### getDerivedStateFromProps 16 | 17 | ```js 18 | function MyApp(props) { 19 | const [factor, setFactor] = useState(1); 20 | 21 | useMemo(() => { 22 | setFactor(props.value); 23 | }, [props.value]); 24 | } 25 | ``` 26 | 27 | ### componentDidMount 28 | 29 | ```js 30 | function MyApp(props) { 31 | const [factor, setFactor] = useState(1); 32 | 33 | useEffect(() => { 34 | // do something similar to mount 35 | }, []); 36 | } 37 | ``` 38 | 39 | ### componentWillUnMount 40 | 41 | ```js 42 | function MyApp(props) { 43 | const [factor, setFactor] = useState(1); 44 | 45 | useEffect(() => { 46 | return () => { 47 | // do something similar to mount 48 | }; 49 | }, []); 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /fiber.md: -------------------------------------------------------------------------------- 1 | You write in JSX. 2 | 3 | ```jsx 4 |
5 |

My App

6 | 7 |
8 | ``` 9 | 10 | JSX is transformed to create element syntax 11 | 12 | ```js 13 | React.createElement( 14 | "div", 15 | { className: "my-app" }, 16 | React.createElement("h1", {}, "My App"), 17 | React.createElement(App, {}) 18 | ); 19 | ``` 20 | 21 | Create element returns react elements 22 | 23 | ```js 24 | { 25 | type: 'div', 26 | props: { 27 | className: 'my-app', 28 | children: [{ 29 | type: 'h1', 30 | props: { 31 | children: 'My App' 32 | } 33 | }, { 34 | type: App, 35 | props: {} 36 | }] 37 | } 38 | } 39 | ``` 40 | 41 | A fiber node is created for each of the React Element 42 | 43 | ```js 44 | // div fiber 45 | { 46 | type: 'div', 47 | stateNode: HTMLElement
, 48 | child: H1Fiber 49 | } 50 | 51 | // h1 fiber 52 | { 53 | type: 'h1', 54 | stateNode: HTMLElement

, 55 | parent: DivFiber, 56 | child: TextFiber, 57 | sibling: AppFiber 58 | } 59 | 60 | // App fiber 61 | { 62 | type: App, 63 | stateNode: AppInstance, // new App 64 | parent: DivFiber, 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## Agenda 2 | 3 | - What are hooks in React? 4 | - Rules of hooks. 5 | - What are the different React hooks and their usage? 6 | - JSX -> VDOM -> Fiber. 7 | - Different phases of rendering. 8 | - How hooks work internally. 9 | - Different parts of React which allow hooks to work. 10 | - Internal implementation of core hooks. 11 | - Why does the dependency array in hooks matter and how does it work? 12 | - Effect of scope in hooks. 13 | - Simulating life cycle methods with hooks. 14 | 15 | ### Resources: 16 | 17 | Rules of Hooks 18 | [https://reactjs.org/docs/hooks-rules.html](https://reactjs.org/docs/hooks-rules.html) 19 | 20 | Scopes and hooks (Making SetInternval declarative with React Hooks) 21 | [https://overreacted.io/making-setinterval-declarative-with-react-hooks/](https://overreacted.io/making-setinterval-declarative-with-react-hooks/) 22 | 23 | A Cartoon Intro to Fiber 24 | [https://www.youtube.com/watch?v=ZCuYPiUIONs](https://www.youtube.com/watch?v=ZCuYPiUIONs) 25 | 26 | Internals meetup recording on Fiber Architecture 27 | [https://www.youtube.com/watch?v=xZMwnZjvZa0](https://www.youtube.com/watch?v=xZMwnZjvZa0) 28 | 29 | React hooks is nothing but an array: 30 | [https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e](https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e) 31 | 32 | Implementation of hooks 33 | [https://www.youtube.com/watch?v=KJP1E-Y-xyo&t=904s](https://www.youtube.com/watch?v=KJP1E-Y-xyo&t=904s) 34 | 35 | Hooks performance Issue 36 | [https://github.com/ryardley/hooks-perf-issues](https://github.com/ryardley/hooks-perf-issues) 37 | 38 | React Specific resources 39 | [https://reactresources.com](https://reactresources.com) 40 | -------------------------------------------------------------------------------- /scopes.md: -------------------------------------------------------------------------------- 1 | ### Basic scope example 2 | 3 | ```js 4 | function ageTeller(name) { 5 | return function (age) { 6 | return `The age of ${name} is ${age}.`; 7 | }; 8 | } 9 | 10 | const sudhanshu = ageTeller("Sudhanshu"); 11 | 12 | sudhanshu(29); // The age of Sudhanshu is 29. 13 | 14 | sudhanshu(30); // The age of Sudhanshu is 30. 15 | ``` 16 | 17 | ### React functional component 18 | 19 | #### Scope example 20 | 21 | ```js 22 | function MyComponent(props) { 23 | const [value, setValue] = useState("initial"); 24 | 25 | useEffect(() => { 26 | document.title = value; 27 | }); 28 | 29 | return
Something
; 30 | } 31 | ``` 32 | 33 | #### Callback example 34 | 35 | ```js ❌ 36 | function MyComponent(props) { 37 | const [value, setValue] = useState("initial"); 38 | 39 | const onClick = useCallback(() => { 40 | setValue(value + "--"); 41 | }, []); 42 | 43 | return ; 44 | } 45 | ``` 46 | 47 | ```js ✅ 48 | function MyComponent(props) { 49 | const [value, setValue] = useState("initial"); 50 | 51 | const onClick = useCallback(() => { 52 | setValue(value + "--"); 53 | }, [value]); 54 | 55 | return ; 56 | } 57 | ``` 58 | 59 | ```js ✅ 60 | function MyComponent(props) { 61 | const [value, setValue] = useState("initial"); 62 | 63 | const onClick = useCallback(() => { 64 | setValue((value) => { 65 | return value + "--"; 66 | }); 67 | }, []); 68 | 69 | return ; 70 | } 71 | ``` 72 | 73 | #### Fetch example 74 | 75 | ```js ❌ 76 | function MyComponent(props) { 77 | const [factor, setFactor] = useState(1); 78 | const [chartData, setChartData] = useState(); 79 | 80 | useEffect(() => { 81 | fetch('chart data').then((list) => { 82 | const factoredList = list.map((list) => list * factor); 83 | setChartData(factoredList); 84 | }) 85 | }, []); 86 | 87 | 88 | return ( 89 | <> 90 | setFactor(Number(e.target.value))} /> 91 | 92 | <> 93 | ) 94 | } 95 | ``` 96 | 97 | ```js ✅ 98 | function MyComponent(props) { 99 | const [factor, setFactor] = useState(1); 100 | const [apiData, setApiData] = useState(); 101 | 102 | useEffect(() => { 103 | fetch('chart data').then((list) => { 104 | setChartData(list); 105 | }) 106 | }, []); 107 | 108 | 109 | const chartData = apiData.map((item) => item * factor); 110 | 111 | //OR 112 | 113 | const chartData = useMemo(() => { 114 | return apiData.map((item) => item * factor) 115 | }, [apiData, factor]) 116 | 117 | return ( 118 | <> 119 | setFactor(Number(e.target.value))} /> 120 | 121 | <> 122 | ) 123 | } 124 | ``` 125 | 126 | #### setInterval example 127 | 128 | ```js ❌ 129 | import React, { useState, useEffect, useRef } from "react"; 130 | 131 | function Counter() { 132 | const [count, setCount] = useState(0); 133 | const [delay, setDelay] = useState(1000); 134 | 135 | useEffect(() => { 136 | setInterval(() => { 137 | setCount(count + 1); 138 | }, delay); 139 | }); 140 | 141 | return ( 142 | <> 143 | setDelay(e.target.value)} 147 | /> 148 |

{count}

; 149 | 150 | ); 151 | } 152 | ``` 153 | 154 | Correct approach 155 | [https://overreacted.io/making-setinterval-declarative-with-react-hooks/](https://overreacted.io/making-setinterval-declarative-with-react-hooks/) 156 | -------------------------------------------------------------------------------- /react.js: -------------------------------------------------------------------------------- 1 | /** @license React vundefined 2 | * react.production.min.js 3 | * 4 | * Copyright (c) Facebook, Inc. and its affiliates. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | */ 9 | (function (global, factory) { 10 | typeof exports === "object" && typeof module !== "undefined" 11 | ? factory(exports) 12 | : typeof define === "function" && define.amd 13 | ? define(["exports"], factory) 14 | : ((global = global || self), factory((global.React = {}))); 15 | })(this, function (exports) { 16 | "use strict"; 17 | 18 | // TODO: this is special because it gets imported during build. 19 | var ReactVersion = "16.13.1"; 20 | 21 | // The Symbol used to tag the ReactElement-like types. If there is no native Symbol 22 | // nor polyfill, then a plain number is used for performance. 23 | let REACT_ELEMENT_TYPE = 0xeac7; 24 | let REACT_PORTAL_TYPE = 0xeaca; 25 | exports.Fragment = 0xeacb; 26 | exports.StrictMode = 0xeacc; 27 | exports.Profiler = 0xead2; 28 | let REACT_PROVIDER_TYPE = 0xeacd; 29 | let REACT_CONTEXT_TYPE = 0xeace; 30 | let REACT_FORWARD_REF_TYPE = 0xead0; 31 | exports.Suspense = 0xead1; 32 | exports.SuspenseList = 0xead8; 33 | let REACT_MEMO_TYPE = 0xead3; 34 | let REACT_LAZY_TYPE = 0xead4; 35 | let REACT_BLOCK_TYPE = 0xead9; 36 | 37 | if (typeof Symbol === "function" && Symbol.for) { 38 | const symbolFor = Symbol.for; 39 | REACT_ELEMENT_TYPE = symbolFor("react.element"); 40 | REACT_PORTAL_TYPE = symbolFor("react.portal"); 41 | exports.Fragment = symbolFor("react.fragment"); 42 | exports.StrictMode = symbolFor("react.strict_mode"); 43 | exports.Profiler = symbolFor("react.profiler"); 44 | REACT_PROVIDER_TYPE = symbolFor("react.provider"); 45 | REACT_CONTEXT_TYPE = symbolFor("react.context"); 46 | REACT_FORWARD_REF_TYPE = symbolFor("react.forward_ref"); 47 | exports.Suspense = symbolFor("react.suspense"); 48 | exports.SuspenseList = symbolFor("react.suspense_list"); 49 | REACT_MEMO_TYPE = symbolFor("react.memo"); 50 | REACT_LAZY_TYPE = symbolFor("react.lazy"); 51 | REACT_BLOCK_TYPE = symbolFor("react.block"); 52 | } 53 | 54 | const MAYBE_ITERATOR_SYMBOL = typeof Symbol === "function" && Symbol.iterator; 55 | const FAUX_ITERATOR_SYMBOL = "@@iterator"; 56 | function getIteratorFn(maybeIterable) { 57 | if (maybeIterable === null || typeof maybeIterable !== "object") { 58 | return null; 59 | } 60 | 61 | const maybeIterator = 62 | (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) || 63 | maybeIterable[FAUX_ITERATOR_SYMBOL]; 64 | 65 | if (typeof maybeIterator === "function") { 66 | return maybeIterator; 67 | } 68 | 69 | return null; 70 | } 71 | 72 | const hasOwnProperty = Object.prototype.hasOwnProperty; 73 | 74 | const _assign = function (to, from) { 75 | for (const key in from) { 76 | if (hasOwnProperty.call(from, key)) { 77 | to[key] = from[key]; 78 | } 79 | } 80 | }; 81 | 82 | var _assign$1 = 83 | Object.assign || 84 | function (target, sources) { 85 | if (target == null) { 86 | throw new TypeError("Object.assign target cannot be null or undefined"); 87 | } 88 | 89 | const to = Object(target); 90 | 91 | for (let nextIndex = 1; nextIndex < arguments.length; nextIndex++) { 92 | const nextSource = arguments[nextIndex]; 93 | 94 | if (nextSource != null) { 95 | _assign(to, Object(nextSource)); 96 | } 97 | } 98 | 99 | return to; 100 | }; 101 | 102 | // Do not require this module directly! Use normal `invariant` calls with 103 | // template literal strings. The messages will be replaced with error codes 104 | // during build. 105 | function formatProdErrorMessage(code) { 106 | let url = "https://reactjs.org/docs/error-decoder.html?invariant=" + code; 107 | 108 | for (let i = 1; i < arguments.length; i++) { 109 | url += "&args[]=" + encodeURIComponent(arguments[i]); 110 | } 111 | 112 | return ( 113 | "Minified React error #" + 114 | code + 115 | "; visit " + 116 | url + 117 | " for the full message or " + 118 | "use the non-minified dev environment for full errors and additional " + 119 | "helpful warnings." 120 | ); 121 | } 122 | 123 | /** 124 | * This is the abstract API for an update queue. 125 | */ 126 | 127 | const ReactNoopUpdateQueue = { 128 | /** 129 | * Checks whether or not this composite component is mounted. 130 | * @param {ReactClass} publicInstance The instance we want to test. 131 | * @return {boolean} True if mounted, false otherwise. 132 | * @protected 133 | * @final 134 | */ 135 | isMounted: function (publicInstance) { 136 | return false; 137 | }, 138 | 139 | /** 140 | * Forces an update. This should only be invoked when it is known with 141 | * certainty that we are **not** in a DOM transaction. 142 | * 143 | * You may want to call this when you know that some deeper aspect of the 144 | * component's state has changed but `setState` was not called. 145 | * 146 | * This will not invoke `shouldComponentUpdate`, but it will invoke 147 | * `componentWillUpdate` and `componentDidUpdate`. 148 | * 149 | * @param {ReactClass} publicInstance The instance that should rerender. 150 | * @param {?function} callback Called after component is updated. 151 | * @param {?string} callerName name of the calling function in the public API. 152 | * @internal 153 | */ 154 | enqueueForceUpdate: function (publicInstance, callback, callerName) {}, 155 | 156 | /** 157 | * Replaces all of the state. Always use this or `setState` to mutate state. 158 | * You should treat `this.state` as immutable. 159 | * 160 | * There is no guarantee that `this.state` will be immediately updated, so 161 | * accessing `this.state` after calling this method may return the old value. 162 | * 163 | * @param {ReactClass} publicInstance The instance that should rerender. 164 | * @param {object} completeState Next state. 165 | * @param {?function} callback Called after component is updated. 166 | * @param {?string} callerName name of the calling function in the public API. 167 | * @internal 168 | */ 169 | enqueueReplaceState: function ( 170 | publicInstance, 171 | completeState, 172 | callback, 173 | callerName 174 | ) {}, 175 | 176 | /** 177 | * Sets a subset of the state. This only exists because _pendingState is 178 | * internal. This provides a merging strategy that is not available to deep 179 | * properties which is confusing. TODO: Expose pendingState or don't use it 180 | * during the merge. 181 | * 182 | * @param {ReactClass} publicInstance The instance that should rerender. 183 | * @param {object} partialState Next partial state to be merged with state. 184 | * @param {?function} callback Called after component is updated. 185 | * @param {?string} Name of the calling function in the public API. 186 | * @internal 187 | */ 188 | enqueueSetState: function ( 189 | publicInstance, 190 | partialState, 191 | callback, 192 | callerName 193 | ) {}, 194 | }; 195 | 196 | const emptyObject = {}; 197 | /** 198 | * Base class helpers for the updating state of a component. 199 | */ 200 | 201 | function Component(props, context, updater) { 202 | this.props = props; 203 | this.context = context; // If a component has string refs, we will assign a different object later. 204 | 205 | this.refs = emptyObject; // We initialize the default updater but the real one gets injected by the 206 | // renderer. 207 | 208 | this.updater = updater || ReactNoopUpdateQueue; 209 | } 210 | 211 | Component.prototype.isReactComponent = {}; 212 | /** 213 | * Sets a subset of the state. Always use this to mutate 214 | * state. You should treat `this.state` as immutable. 215 | * 216 | * There is no guarantee that `this.state` will be immediately updated, so 217 | * accessing `this.state` after calling this method may return the old value. 218 | * 219 | * There is no guarantee that calls to `setState` will run synchronously, 220 | * as they may eventually be batched together. You can provide an optional 221 | * callback that will be executed when the call to setState is actually 222 | * completed. 223 | * 224 | * When a function is provided to setState, it will be called at some point in 225 | * the future (not synchronously). It will be called with the up to date 226 | * component arguments (state, props, context). These values can be different 227 | * from this.* because your function may be called after receiveProps but before 228 | * shouldComponentUpdate, and this new state, props, and context will not yet be 229 | * assigned to this. 230 | * 231 | * @param {object|function} partialState Next partial state or function to 232 | * produce next partial state to be merged with current state. 233 | * @param {?function} callback Called after state is updated. 234 | * @final 235 | * @protected 236 | */ 237 | 238 | Component.prototype.setState = function (partialState, callback) { 239 | if ( 240 | !( 241 | typeof partialState === "object" || 242 | typeof partialState === "function" || 243 | partialState == null 244 | ) 245 | ) { 246 | { 247 | throw Error(formatProdErrorMessage(85)); 248 | } 249 | } 250 | 251 | this.updater.enqueueSetState(this, partialState, callback, "setState"); 252 | }; 253 | /** 254 | * Forces an update. This should only be invoked when it is known with 255 | * certainty that we are **not** in a DOM transaction. 256 | * 257 | * You may want to call this when you know that some deeper aspect of the 258 | * component's state has changed but `setState` was not called. 259 | * 260 | * This will not invoke `shouldComponentUpdate`, but it will invoke 261 | * `componentWillUpdate` and `componentDidUpdate`. 262 | * 263 | * @param {?function} callback Called after update is complete. 264 | * @final 265 | * @protected 266 | */ 267 | 268 | Component.prototype.forceUpdate = function (callback) { 269 | this.updater.enqueueForceUpdate(this, callback, "forceUpdate"); 270 | }; 271 | 272 | function ComponentDummy() {} 273 | 274 | ComponentDummy.prototype = Component.prototype; 275 | /** 276 | * Convenience component with default shallow equality check for sCU. 277 | */ 278 | 279 | function PureComponent(props, context, updater) { 280 | this.props = props; 281 | this.context = context; // If a component has string refs, we will assign a different object later. 282 | 283 | this.refs = emptyObject; 284 | this.updater = updater || ReactNoopUpdateQueue; 285 | } 286 | 287 | const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy()); 288 | pureComponentPrototype.constructor = PureComponent; // Avoid an extra prototype jump for these methods. 289 | 290 | _assign$1(pureComponentPrototype, Component.prototype); 291 | 292 | pureComponentPrototype.isPureReactComponent = true; 293 | 294 | // an immutable object with a single mutable value 295 | function createRef() { 296 | const refObject = { 297 | current: null, 298 | }; 299 | 300 | return refObject; 301 | } 302 | 303 | /** 304 | * Keeps track of the current owner. 305 | * 306 | * The current owner is the component who should own any components that are 307 | * currently being constructed. 308 | */ 309 | const ReactCurrentOwner = { 310 | /** 311 | * @internal 312 | * @type {ReactComponent} 313 | */ 314 | current: null, 315 | }; 316 | 317 | const hasOwnProperty$1 = Object.prototype.hasOwnProperty; 318 | const RESERVED_PROPS = { 319 | key: true, 320 | ref: true, 321 | __self: true, 322 | __source: true, 323 | }; 324 | 325 | function hasValidRef(config) { 326 | return config.ref !== undefined; 327 | } 328 | 329 | function hasValidKey(config) { 330 | return config.key !== undefined; 331 | } 332 | /** 333 | * Factory method to create a new React element. This no longer adheres to 334 | * the class pattern, so do not use new to call it. Also, instanceof check 335 | * will not work. Instead test $$typeof field against Symbol.for('react.element') to check 336 | * if something is a React Element. 337 | * 338 | * @param {*} type 339 | * @param {*} props 340 | * @param {*} key 341 | * @param {string|object} ref 342 | * @param {*} owner 343 | * @param {*} self A *temporary* helper to detect places where `this` is 344 | * different from the `owner` when React.createElement is called, so that we 345 | * can warn. We want to get rid of owner and replace string `ref`s with arrow 346 | * functions, and as long as `this` and owner are the same, there will be no 347 | * change in behavior. 348 | * @param {*} source An annotation object (added by a transpiler or otherwise) 349 | * indicating filename, line number, and/or other information. 350 | * @internal 351 | */ 352 | 353 | const ReactElement = function (type, key, ref, self, source, owner, props) { 354 | const element = { 355 | // This tag allows us to uniquely identify this as a React Element 356 | $$typeof: REACT_ELEMENT_TYPE, 357 | // Built-in properties that belong on the element 358 | type: type, 359 | key: key, 360 | ref: ref, 361 | props: props, 362 | // Record the component responsible for creating this element. 363 | _owner: owner, 364 | }; 365 | 366 | return element; 367 | }; 368 | /** 369 | * Create and return a new ReactElement of the given type. 370 | * See https://reactjs.org/docs/react-api.html#createelement 371 | */ 372 | 373 | function createElement(type, config, children) { 374 | let propName; // Reserved names are extracted 375 | 376 | const props = {}; 377 | let key = null; 378 | let ref = null; 379 | let self = null; 380 | let source = null; 381 | 382 | if (config != null) { 383 | if (hasValidRef(config)) { 384 | ref = config.ref; 385 | } 386 | 387 | if (hasValidKey(config)) { 388 | key = "" + config.key; 389 | } 390 | 391 | self = config.__self === undefined ? null : config.__self; 392 | source = config.__source === undefined ? null : config.__source; // Remaining properties are added to a new props object 393 | 394 | for (propName in config) { 395 | if ( 396 | hasOwnProperty$1.call(config, propName) && 397 | !RESERVED_PROPS.hasOwnProperty(propName) 398 | ) { 399 | props[propName] = config[propName]; 400 | } 401 | } 402 | } // Children can be more than one argument, and those are transferred onto 403 | // the newly allocated props object. 404 | 405 | const childrenLength = arguments.length - 2; 406 | 407 | if (childrenLength === 1) { 408 | props.children = children; 409 | } else if (childrenLength > 1) { 410 | const childArray = Array(childrenLength); 411 | 412 | for (let i = 0; i < childrenLength; i++) { 413 | childArray[i] = arguments[i + 2]; 414 | } 415 | 416 | props.children = childArray; 417 | } // Resolve default props 418 | 419 | if (type && type.defaultProps) { 420 | const defaultProps = type.defaultProps; 421 | 422 | for (propName in defaultProps) { 423 | if (props[propName] === undefined) { 424 | props[propName] = defaultProps[propName]; 425 | } 426 | } 427 | } 428 | 429 | return ReactElement( 430 | type, 431 | key, 432 | ref, 433 | self, 434 | source, 435 | ReactCurrentOwner.current, 436 | props 437 | ); 438 | } 439 | /** 440 | * Return a function that produces ReactElements of a given type. 441 | * See https://reactjs.org/docs/react-api.html#createfactory 442 | */ 443 | 444 | function createFactory(type) { 445 | const factory = createElement.bind(null, type); // Expose the type on the factory and the prototype so that it can be 446 | // easily accessed on elements. E.g. `.type === Foo`. 447 | // This should not be named `constructor` since this may not be the function 448 | // that created the element, and it may not even be a constructor. 449 | // Legacy hook: remove it 450 | 451 | factory.type = type; 452 | return factory; 453 | } 454 | function cloneAndReplaceKey(oldElement, newKey) { 455 | const newElement = ReactElement( 456 | oldElement.type, 457 | newKey, 458 | oldElement.ref, 459 | oldElement._self, 460 | oldElement._source, 461 | oldElement._owner, 462 | oldElement.props 463 | ); 464 | return newElement; 465 | } 466 | /** 467 | * Clone and return a new ReactElement using element as the starting point. 468 | * See https://reactjs.org/docs/react-api.html#cloneelement 469 | */ 470 | 471 | function cloneElement(element, config, children) { 472 | if (!!(element === null || element === undefined)) { 473 | { 474 | throw Error(formatProdErrorMessage(267, element)); 475 | } 476 | } 477 | 478 | let propName; // Original props are copied 479 | 480 | const props = _assign$1({}, element.props); // Reserved names are extracted 481 | 482 | let key = element.key; 483 | let ref = element.ref; // Self is preserved since the owner is preserved. 484 | 485 | const self = element._self; // Source is preserved since cloneElement is unlikely to be targeted by a 486 | // transpiler, and the original source is probably a better indicator of the 487 | // true owner. 488 | 489 | const source = element._source; // Owner will be preserved, unless ref is overridden 490 | 491 | let owner = element._owner; 492 | 493 | if (config != null) { 494 | if (hasValidRef(config)) { 495 | // Silently steal the ref from the parent. 496 | ref = config.ref; 497 | owner = ReactCurrentOwner.current; 498 | } 499 | 500 | if (hasValidKey(config)) { 501 | key = "" + config.key; 502 | } // Remaining properties override existing props 503 | 504 | let defaultProps; 505 | 506 | if (element.type && element.type.defaultProps) { 507 | defaultProps = element.type.defaultProps; 508 | } 509 | 510 | for (propName in config) { 511 | if ( 512 | hasOwnProperty$1.call(config, propName) && 513 | !RESERVED_PROPS.hasOwnProperty(propName) 514 | ) { 515 | if (config[propName] === undefined && defaultProps !== undefined) { 516 | // Resolve default props 517 | props[propName] = defaultProps[propName]; 518 | } else { 519 | props[propName] = config[propName]; 520 | } 521 | } 522 | } 523 | } // Children can be more than one argument, and those are transferred onto 524 | // the newly allocated props object. 525 | 526 | const childrenLength = arguments.length - 2; 527 | 528 | if (childrenLength === 1) { 529 | props.children = children; 530 | } else if (childrenLength > 1) { 531 | const childArray = Array(childrenLength); 532 | 533 | for (let i = 0; i < childrenLength; i++) { 534 | childArray[i] = arguments[i + 2]; 535 | } 536 | 537 | props.children = childArray; 538 | } 539 | 540 | return ReactElement(element.type, key, ref, self, source, owner, props); 541 | } 542 | /** 543 | * Verifies the object is a ReactElement. 544 | * See https://reactjs.org/docs/react-api.html#isvalidelement 545 | * @param {?object} object 546 | * @return {boolean} True if `object` is a ReactElement. 547 | * @final 548 | */ 549 | 550 | function isValidElement(object) { 551 | return ( 552 | typeof object === "object" && 553 | object !== null && 554 | object.$$typeof === REACT_ELEMENT_TYPE 555 | ); 556 | } 557 | 558 | const SEPARATOR = "."; 559 | const SUBSEPARATOR = ":"; 560 | /** 561 | * Escape and wrap key so it is safe to use as a reactid 562 | * 563 | * @param {string} key to be escaped. 564 | * @return {string} the escaped key. 565 | */ 566 | 567 | function escape(key) { 568 | const escapeRegex = /[=:]/g; 569 | const escaperLookup = { 570 | "=": "=0", 571 | ":": "=2", 572 | }; 573 | const escapedString = key.replace(escapeRegex, function (match) { 574 | return escaperLookup[match]; 575 | }); 576 | return "$" + escapedString; 577 | } 578 | const userProvidedKeyEscapeRegex = /\/+/g; 579 | 580 | function escapeUserProvidedKey(text) { 581 | return text.replace(userProvidedKeyEscapeRegex, "$&/"); 582 | } 583 | /** 584 | * Generate a key string that identifies a element within a set. 585 | * 586 | * @param {*} element A element that could contain a manual key. 587 | * @param {number} index Index that is used if a manual key is not provided. 588 | * @return {string} 589 | */ 590 | 591 | function getElementKey(element, index) { 592 | // Do some typechecking here since we call this blindly. We want to ensure 593 | // that we don't block potential future ES APIs. 594 | if ( 595 | typeof element === "object" && 596 | element !== null && 597 | element.key != null 598 | ) { 599 | // Explicit key 600 | return escape("" + element.key); 601 | } // Implicit key determined by the index in the set 602 | 603 | return index.toString(36); 604 | } 605 | 606 | function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) { 607 | const type = typeof children; 608 | 609 | if (type === "undefined" || type === "boolean") { 610 | // All of the above are perceived as null. 611 | children = null; 612 | } 613 | 614 | let invokeCallback = false; 615 | 616 | if (children === null) { 617 | invokeCallback = true; 618 | } else { 619 | switch (type) { 620 | case "string": 621 | case "number": 622 | invokeCallback = true; 623 | break; 624 | 625 | case "object": 626 | switch (children.$$typeof) { 627 | case REACT_ELEMENT_TYPE: 628 | case REACT_PORTAL_TYPE: 629 | invokeCallback = true; 630 | } 631 | } 632 | } 633 | 634 | if (invokeCallback) { 635 | const child = children; 636 | let mappedChild = callback(child); // If it's the only child, treat the name as if it was wrapped in an array 637 | // so that it's consistent if the number of children grows: 638 | 639 | const childKey = 640 | nameSoFar === "" ? SEPARATOR + getElementKey(child, 0) : nameSoFar; 641 | 642 | if (Array.isArray(mappedChild)) { 643 | let escapedChildKey = ""; 644 | 645 | if (childKey != null) { 646 | escapedChildKey = escapeUserProvidedKey(childKey) + "/"; 647 | } 648 | 649 | mapIntoArray(mappedChild, array, escapedChildKey, "", (c) => c); 650 | } else if (mappedChild != null) { 651 | if (isValidElement(mappedChild)) { 652 | mappedChild = cloneAndReplaceKey( 653 | mappedChild, // Keep both the (mapped) and old keys if they differ, just as 654 | // traverseAllChildren used to do for objects as children 655 | escapedPrefix + // $FlowFixMe Flow incorrectly thinks React.Portal doesn't have a key 656 | (mappedChild.key && (!child || child.key !== mappedChild.key) // $FlowFixMe Flow incorrectly thinks existing element's key can be a number 657 | ? escapeUserProvidedKey("" + mappedChild.key) + "/" 658 | : "") + 659 | childKey 660 | ); 661 | } 662 | 663 | array.push(mappedChild); 664 | } 665 | 666 | return 1; 667 | } 668 | 669 | let child; 670 | let nextName; 671 | let subtreeCount = 0; // Count of children found in the current subtree. 672 | 673 | const nextNamePrefix = 674 | nameSoFar === "" ? SEPARATOR : nameSoFar + SUBSEPARATOR; 675 | 676 | if (Array.isArray(children)) { 677 | for (let i = 0; i < children.length; i++) { 678 | child = children[i]; 679 | nextName = nextNamePrefix + getElementKey(child, i); 680 | subtreeCount += mapIntoArray( 681 | child, 682 | array, 683 | escapedPrefix, 684 | nextName, 685 | callback 686 | ); 687 | } 688 | } else { 689 | const iteratorFn = getIteratorFn(children); 690 | 691 | if (typeof iteratorFn === "function") { 692 | const iterableChildren = children; 693 | 694 | const iterator = iteratorFn.call(iterableChildren); 695 | let step; 696 | let ii = 0; 697 | 698 | while (!(step = iterator.next()).done) { 699 | child = step.value; 700 | nextName = nextNamePrefix + getElementKey(child, ii++); 701 | subtreeCount += mapIntoArray( 702 | child, 703 | array, 704 | escapedPrefix, 705 | nextName, 706 | callback 707 | ); 708 | } 709 | } else if (type === "object") { 710 | let addendum = ""; 711 | 712 | const childrenString = "" + children; 713 | 714 | { 715 | { 716 | throw Error( 717 | formatProdErrorMessage( 718 | 31, 719 | childrenString === "[object Object]" 720 | ? "object with keys {" + 721 | Object.keys(children).join(", ") + 722 | "}" 723 | : childrenString, 724 | addendum 725 | ) 726 | ); 727 | } 728 | } 729 | } 730 | } 731 | 732 | return subtreeCount; 733 | } 734 | 735 | /** 736 | * Maps children that are typically specified as `props.children`. 737 | * 738 | * See https://reactjs.org/docs/react-api.html#reactchildrenmap 739 | * 740 | * The provided mapFunction(child, key, index) will be called for each 741 | * leaf child. 742 | * 743 | * @param {?*} children Children tree container. 744 | * @param {function(*, int)} func The map function. 745 | * @param {*} context Context for mapFunction. 746 | * @return {object} Object containing the ordered map of results. 747 | */ 748 | function mapChildren(children, func, context) { 749 | if (children == null) { 750 | return children; 751 | } 752 | 753 | const result = []; 754 | let count = 0; 755 | mapIntoArray(children, result, "", "", function (child) { 756 | return func.call(context, child, count++); 757 | }); 758 | return result; 759 | } 760 | /** 761 | * Count the number of children that are typically specified as 762 | * `props.children`. 763 | * 764 | * See https://reactjs.org/docs/react-api.html#reactchildrencount 765 | * 766 | * @param {?*} children Children tree container. 767 | * @return {number} The number of children. 768 | */ 769 | 770 | function countChildren(children) { 771 | let n = 0; 772 | mapChildren(children, () => { 773 | n++; // Don't return anything 774 | }); 775 | return n; 776 | } 777 | 778 | /** 779 | * Iterates through children that are typically specified as `props.children`. 780 | * 781 | * See https://reactjs.org/docs/react-api.html#reactchildrenforeach 782 | * 783 | * The provided forEachFunc(child, index) will be called for each 784 | * leaf child. 785 | * 786 | * @param {?*} children Children tree container. 787 | * @param {function(*, int)} forEachFunc 788 | * @param {*} forEachContext Context for forEachContext. 789 | */ 790 | function forEachChildren(children, forEachFunc, forEachContext) { 791 | mapChildren( 792 | children, 793 | function () { 794 | forEachFunc.apply(this, arguments); // Don't return anything. 795 | }, 796 | forEachContext 797 | ); 798 | } 799 | /** 800 | * Flatten a children object (typically specified as `props.children`) and 801 | * return an array with appropriately re-keyed children. 802 | * 803 | * See https://reactjs.org/docs/react-api.html#reactchildrentoarray 804 | */ 805 | 806 | function toArray(children) { 807 | return mapChildren(children, (child) => child) || []; 808 | } 809 | /** 810 | * Returns the first child in a collection of children and verifies that there 811 | * is only one child in the collection. 812 | * 813 | * See https://reactjs.org/docs/react-api.html#reactchildrenonly 814 | * 815 | * The current implementation of this function assumes that a single child gets 816 | * passed without a wrapper, but the purpose of this helper function is to 817 | * abstract away the particular structure of children. 818 | * 819 | * @param {?object} children Child collection structure. 820 | * @return {ReactElement} The first and only `ReactElement` contained in the 821 | * structure. 822 | */ 823 | 824 | function onlyChild(children) { 825 | if (!isValidElement(children)) { 826 | { 827 | throw Error(formatProdErrorMessage(143)); 828 | } 829 | } 830 | 831 | return children; 832 | } 833 | 834 | function createContext(defaultValue, calculateChangedBits) { 835 | if (calculateChangedBits === undefined) { 836 | calculateChangedBits = null; 837 | } 838 | 839 | const context = { 840 | $$typeof: REACT_CONTEXT_TYPE, 841 | _calculateChangedBits: calculateChangedBits, 842 | // As a workaround to support multiple concurrent renderers, we categorize 843 | // some renderers as primary and others as secondary. We only expect 844 | // there to be two concurrent renderers at most: React Native (primary) and 845 | // Fabric (secondary); React DOM (primary) and React ART (secondary). 846 | // Secondary renderers store their context values on separate fields. 847 | _currentValue: defaultValue, 848 | _currentValue2: defaultValue, 849 | // Used to track how many concurrent renderers this context currently 850 | // supports within in a single renderer. Such as parallel server rendering. 851 | _threadCount: 0, 852 | // These are circular 853 | Provider: null, 854 | Consumer: null, 855 | }; 856 | context.Provider = { 857 | $$typeof: REACT_PROVIDER_TYPE, 858 | _context: context, 859 | }; 860 | 861 | { 862 | context.Consumer = context; 863 | } 864 | 865 | return context; 866 | } 867 | 868 | const Uninitialized = -1; 869 | const Pending = 0; 870 | const Resolved = 1; 871 | const Rejected = 2; 872 | 873 | function lazyInitializer(payload) { 874 | if (payload._status === Uninitialized) { 875 | const ctor = payload._result; 876 | const thenable = ctor(); // Transition to the next state. 877 | 878 | const pending = payload; 879 | pending._status = Pending; 880 | pending._result = thenable; 881 | thenable.then( 882 | (moduleObject) => { 883 | if (payload._status === Pending) { 884 | const defaultExport = moduleObject.default; 885 | 886 | const resolved = payload; 887 | resolved._status = Resolved; 888 | resolved._result = defaultExport; 889 | } 890 | }, 891 | (error) => { 892 | if (payload._status === Pending) { 893 | // Transition to the next state. 894 | const rejected = payload; 895 | rejected._status = Rejected; 896 | rejected._result = error; 897 | } 898 | } 899 | ); 900 | } 901 | 902 | if (payload._status === Resolved) { 903 | return payload._result; 904 | } else { 905 | throw payload._result; 906 | } 907 | } 908 | 909 | function lazy(ctor) { 910 | const payload = { 911 | // We use these fields to store the result. 912 | _status: -1, 913 | _result: ctor, 914 | }; 915 | const lazyType = { 916 | $$typeof: REACT_LAZY_TYPE, 917 | _payload: payload, 918 | _init: lazyInitializer, 919 | }; 920 | 921 | return lazyType; 922 | } 923 | 924 | function forwardRef(render) { 925 | const elementType = { 926 | $$typeof: REACT_FORWARD_REF_TYPE, 927 | render, 928 | }; 929 | 930 | return elementType; 931 | } 932 | 933 | function memo(type, compare) { 934 | const elementType = { 935 | $$typeof: REACT_MEMO_TYPE, 936 | type, 937 | compare: compare === undefined ? null : compare, 938 | }; 939 | 940 | return elementType; 941 | } 942 | 943 | function lazyInitializer$1(payload) { 944 | return { 945 | $$typeof: REACT_BLOCK_TYPE, 946 | _data: payload.load.apply(null, payload.args), 947 | _render: payload.render, 948 | }; 949 | } 950 | 951 | function block(render, load) { 952 | if (load === undefined) { 953 | return function () { 954 | const blockComponent = { 955 | $$typeof: REACT_BLOCK_TYPE, 956 | _data: undefined, 957 | // $FlowFixMe: Data must be void in this scenario. 958 | _render: render, 959 | }; // $FlowFixMe: Upstream BlockComponent to Flow as a valid Node. 960 | 961 | return blockComponent; 962 | }; 963 | } // Trick to let Flow refine this. 964 | 965 | const loadFn = load; 966 | return function () { 967 | const args = arguments; 968 | const payload = { 969 | load: loadFn, 970 | args: args, 971 | render: render, 972 | }; 973 | const lazyType = { 974 | $$typeof: REACT_LAZY_TYPE, 975 | _payload: payload, 976 | _init: lazyInitializer$1, 977 | }; // $FlowFixMe: Upstream BlockComponent to Flow as a valid Node. 978 | 979 | return lazyType; 980 | }; 981 | } 982 | 983 | /** 984 | * Keeps track of the current dispatcher. 985 | */ 986 | const ReactCurrentDispatcher = { 987 | /** 988 | * @internal 989 | * @type {ReactComponent} 990 | */ 991 | current: null, 992 | }; 993 | 994 | function resolveDispatcher() { 995 | const dispatcher = ReactCurrentDispatcher.current; 996 | 997 | if (!(dispatcher !== null)) { 998 | { 999 | throw Error(formatProdErrorMessage(321)); 1000 | } 1001 | } 1002 | 1003 | return dispatcher; 1004 | } 1005 | 1006 | function useContext(Context, unstable_observedBits) { 1007 | const dispatcher = resolveDispatcher(); 1008 | 1009 | return dispatcher.useContext(Context, unstable_observedBits); 1010 | } 1011 | function useState(initialState) { 1012 | const dispatcher = resolveDispatcher(); 1013 | return dispatcher.useState(initialState); 1014 | } 1015 | function useReducer(reducer, initialArg, init) { 1016 | const dispatcher = resolveDispatcher(); 1017 | return dispatcher.useReducer(reducer, initialArg, init); 1018 | } 1019 | function useRef(initialValue) { 1020 | const dispatcher = resolveDispatcher(); 1021 | return dispatcher.useRef(initialValue); 1022 | } 1023 | function useEffect(create, deps) { 1024 | const dispatcher = resolveDispatcher(); 1025 | return dispatcher.useEffect(create, deps); 1026 | } 1027 | function useLayoutEffect(create, deps) { 1028 | const dispatcher = resolveDispatcher(); 1029 | return dispatcher.useLayoutEffect(create, deps); 1030 | } 1031 | function useCallback(callback, deps) { 1032 | const dispatcher = resolveDispatcher(); 1033 | return dispatcher.useCallback(callback, deps); 1034 | } 1035 | function useMemo(create, deps) { 1036 | const dispatcher = resolveDispatcher(); 1037 | return dispatcher.useMemo(create, deps); 1038 | } 1039 | function useImperativeHandle(ref, create, deps) { 1040 | const dispatcher = resolveDispatcher(); 1041 | return dispatcher.useImperativeHandle(ref, create, deps); 1042 | } 1043 | function useDebugValue(value, formatterFn) {} 1044 | function useTransition(config) { 1045 | const dispatcher = resolveDispatcher(); 1046 | return dispatcher.useTransition(config); 1047 | } 1048 | function useDeferredValue(value, config) { 1049 | const dispatcher = resolveDispatcher(); 1050 | return dispatcher.useDeferredValue(value, config); 1051 | } 1052 | function useOpaqueIdentifier() { 1053 | const dispatcher = resolveDispatcher(); 1054 | return dispatcher.useOpaqueIdentifier(); 1055 | } 1056 | function useMutableSource(source, getSnapshot, subscribe) { 1057 | const dispatcher = resolveDispatcher(); 1058 | return dispatcher.useMutableSource(source, getSnapshot, subscribe); 1059 | } 1060 | 1061 | /** 1062 | * Keeps track of the current batch's configuration such as how long an update 1063 | * should suspend for if it needs to. 1064 | */ 1065 | const ReactCurrentBatchConfig = { 1066 | suspense: null, 1067 | }; 1068 | 1069 | function withSuspenseConfig(scope, config) { 1070 | const previousConfig = ReactCurrentBatchConfig.suspense; 1071 | ReactCurrentBatchConfig.suspense = config === undefined ? null : config; 1072 | 1073 | try { 1074 | scope(); 1075 | } finally { 1076 | ReactCurrentBatchConfig.suspense = previousConfig; 1077 | } 1078 | } 1079 | 1080 | function createMutableSource(source, getVersion) { 1081 | const mutableSource = { 1082 | _getVersion: getVersion, 1083 | _source: source, 1084 | _workInProgressVersionPrimary: null, 1085 | _workInProgressVersionSecondary: null, 1086 | }; 1087 | 1088 | return mutableSource; 1089 | } 1090 | 1091 | const enableSchedulerDebugging = false; 1092 | const enableProfiling = false; 1093 | 1094 | let requestHostCallback; 1095 | let requestHostTimeout; 1096 | let cancelHostTimeout; 1097 | let shouldYieldToHost; 1098 | let requestPaint; 1099 | let getCurrentTime; 1100 | let forceFrameRate; 1101 | 1102 | if ( 1103 | // If Scheduler runs in a non-DOM environment, it falls back to a naive 1104 | // implementation using setTimeout. 1105 | typeof window === "undefined" || // Check if MessageChannel is supported, too. 1106 | typeof MessageChannel !== "function" 1107 | ) { 1108 | // If this accidentally gets imported in a non-browser environment, e.g. JavaScriptCore, 1109 | // fallback to a naive implementation. 1110 | let _callback = null; 1111 | let _timeoutID = null; 1112 | 1113 | const _flushCallback = function () { 1114 | if (_callback !== null) { 1115 | try { 1116 | const currentTime = getCurrentTime(); 1117 | const hasRemainingTime = true; 1118 | 1119 | _callback(hasRemainingTime, currentTime); 1120 | 1121 | _callback = null; 1122 | } catch (e) { 1123 | setTimeout(_flushCallback, 0); 1124 | throw e; 1125 | } 1126 | } 1127 | }; 1128 | 1129 | const initialTime = Date.now(); 1130 | 1131 | getCurrentTime = function () { 1132 | return Date.now() - initialTime; 1133 | }; 1134 | 1135 | requestHostCallback = function (cb) { 1136 | if (_callback !== null) { 1137 | // Protect against re-entrancy. 1138 | setTimeout(requestHostCallback, 0, cb); 1139 | } else { 1140 | _callback = cb; 1141 | setTimeout(_flushCallback, 0); 1142 | } 1143 | }; 1144 | 1145 | requestHostTimeout = function (cb, ms) { 1146 | _timeoutID = setTimeout(cb, ms); 1147 | }; 1148 | 1149 | cancelHostTimeout = function () { 1150 | clearTimeout(_timeoutID); 1151 | }; 1152 | 1153 | shouldYieldToHost = function () { 1154 | return false; 1155 | }; 1156 | 1157 | requestPaint = forceFrameRate = function () {}; 1158 | } else { 1159 | // Capture local references to native APIs, in case a polyfill overrides them. 1160 | const performance = window.performance; 1161 | const Date = window.Date; 1162 | const setTimeout = window.setTimeout; 1163 | const clearTimeout = window.clearTimeout; 1164 | 1165 | if (typeof console !== "undefined") { 1166 | // TODO: Scheduler no longer requires these methods to be polyfilled. But 1167 | // maybe we want to continue warning if they don't exist, to preserve the 1168 | // option to rely on it in the future? 1169 | const requestAnimationFrame = window.requestAnimationFrame; 1170 | const cancelAnimationFrame = window.cancelAnimationFrame; // TODO: Remove fb.me link 1171 | 1172 | if (typeof requestAnimationFrame !== "function") { 1173 | // Using console['error'] to evade Babel and ESLint 1174 | console["error"]( 1175 | "This browser doesn't support requestAnimationFrame. " + 1176 | "Make sure that you load a " + 1177 | "polyfill in older browsers. https://fb.me/react-polyfills" 1178 | ); 1179 | } 1180 | 1181 | if (typeof cancelAnimationFrame !== "function") { 1182 | // Using console['error'] to evade Babel and ESLint 1183 | console["error"]( 1184 | "This browser doesn't support cancelAnimationFrame. " + 1185 | "Make sure that you load a " + 1186 | "polyfill in older browsers. https://fb.me/react-polyfills" 1187 | ); 1188 | } 1189 | } 1190 | 1191 | if ( 1192 | typeof performance === "object" && 1193 | typeof performance.now === "function" 1194 | ) { 1195 | getCurrentTime = () => performance.now(); 1196 | } else { 1197 | const initialTime = Date.now(); 1198 | 1199 | getCurrentTime = () => Date.now() - initialTime; 1200 | } 1201 | 1202 | let isMessageLoopRunning = false; 1203 | let scheduledHostCallback = null; 1204 | let taskTimeoutID = -1; // Scheduler periodically yields in case there is other work on the main 1205 | // thread, like user events. By default, it yields multiple times per frame. 1206 | // It does not attempt to align with frame boundaries, since most tasks don't 1207 | // need to be frame aligned; for those that do, use requestAnimationFrame. 1208 | 1209 | let yieldInterval = 5; 1210 | let deadline = 0; // TODO: Make this configurable 1211 | 1212 | { 1213 | // `isInputPending` is not available. Since we have no way of knowing if 1214 | // there's pending input, always yield at the end of the frame. 1215 | shouldYieldToHost = function () { 1216 | return getCurrentTime() >= deadline; 1217 | }; // Since we yield every frame regardless, `requestPaint` has no effect. 1218 | 1219 | requestPaint = function () {}; 1220 | } 1221 | 1222 | forceFrameRate = function (fps) { 1223 | if (fps < 0 || fps > 125) { 1224 | // Using console['error'] to evade Babel and ESLint 1225 | console["error"]( 1226 | "forceFrameRate takes a positive int between 0 and 125, " + 1227 | "forcing framerates higher than 125 fps is not unsupported" 1228 | ); 1229 | return; 1230 | } 1231 | 1232 | if (fps > 0) { 1233 | yieldInterval = Math.floor(1000 / fps); 1234 | } else { 1235 | // reset the framerate 1236 | yieldInterval = 5; 1237 | } 1238 | }; 1239 | 1240 | const performWorkUntilDeadline = () => { 1241 | if (scheduledHostCallback !== null) { 1242 | const currentTime = getCurrentTime(); // Yield after `yieldInterval` ms, regardless of where we are in the vsync 1243 | // cycle. This means there's always time remaining at the beginning of 1244 | // the message event. 1245 | 1246 | deadline = currentTime + yieldInterval; 1247 | const hasTimeRemaining = true; 1248 | 1249 | try { 1250 | const hasMoreWork = scheduledHostCallback( 1251 | hasTimeRemaining, 1252 | currentTime 1253 | ); 1254 | 1255 | if (!hasMoreWork) { 1256 | isMessageLoopRunning = false; 1257 | scheduledHostCallback = null; 1258 | } else { 1259 | // If there's more work, schedule the next message event at the end 1260 | // of the preceding one. 1261 | port.postMessage(null); 1262 | } 1263 | } catch (error) { 1264 | // If a scheduler task throws, exit the current browser task so the 1265 | // error can be observed. 1266 | port.postMessage(null); 1267 | throw error; 1268 | } 1269 | } else { 1270 | isMessageLoopRunning = false; 1271 | } // Yielding to the browser will give it a chance to paint, so we can 1272 | }; 1273 | 1274 | const channel = new MessageChannel(); 1275 | const port = channel.port2; 1276 | channel.port1.onmessage = performWorkUntilDeadline; 1277 | 1278 | requestHostCallback = function (callback) { 1279 | scheduledHostCallback = callback; 1280 | 1281 | if (!isMessageLoopRunning) { 1282 | isMessageLoopRunning = true; 1283 | port.postMessage(null); 1284 | } 1285 | }; 1286 | 1287 | requestHostTimeout = function (callback, ms) { 1288 | taskTimeoutID = setTimeout(() => { 1289 | callback(getCurrentTime()); 1290 | }, ms); 1291 | }; 1292 | 1293 | cancelHostTimeout = function () { 1294 | clearTimeout(taskTimeoutID); 1295 | taskTimeoutID = -1; 1296 | }; 1297 | } 1298 | 1299 | function push(heap, node) { 1300 | const index = heap.length; 1301 | heap.push(node); 1302 | siftUp(heap, node, index); 1303 | } 1304 | function peek(heap) { 1305 | const first = heap[0]; 1306 | return first === undefined ? null : first; 1307 | } 1308 | function pop(heap) { 1309 | const first = heap[0]; 1310 | 1311 | if (first !== undefined) { 1312 | const last = heap.pop(); 1313 | 1314 | if (last !== first) { 1315 | heap[0] = last; 1316 | siftDown(heap, last, 0); 1317 | } 1318 | 1319 | return first; 1320 | } else { 1321 | return null; 1322 | } 1323 | } 1324 | 1325 | function siftUp(heap, node, i) { 1326 | let index = i; 1327 | 1328 | while (true) { 1329 | const parentIndex = (index - 1) >>> 1; 1330 | const parent = heap[parentIndex]; 1331 | 1332 | if (parent !== undefined && compare(parent, node) > 0) { 1333 | // The parent is larger. Swap positions. 1334 | heap[parentIndex] = node; 1335 | heap[index] = parent; 1336 | index = parentIndex; 1337 | } else { 1338 | // The parent is smaller. Exit. 1339 | return; 1340 | } 1341 | } 1342 | } 1343 | 1344 | function siftDown(heap, node, i) { 1345 | let index = i; 1346 | const length = heap.length; 1347 | 1348 | while (index < length) { 1349 | const leftIndex = (index + 1) * 2 - 1; 1350 | const left = heap[leftIndex]; 1351 | const rightIndex = leftIndex + 1; 1352 | const right = heap[rightIndex]; // If the left or right node is smaller, swap with the smaller of those. 1353 | 1354 | if (left !== undefined && compare(left, node) < 0) { 1355 | if (right !== undefined && compare(right, left) < 0) { 1356 | heap[index] = right; 1357 | heap[rightIndex] = node; 1358 | index = rightIndex; 1359 | } else { 1360 | heap[index] = left; 1361 | heap[leftIndex] = node; 1362 | index = leftIndex; 1363 | } 1364 | } else if (right !== undefined && compare(right, node) < 0) { 1365 | heap[index] = right; 1366 | heap[rightIndex] = node; 1367 | index = rightIndex; 1368 | } else { 1369 | // Neither child is smaller. Exit. 1370 | return; 1371 | } 1372 | } 1373 | } 1374 | 1375 | function compare(a, b) { 1376 | // Compare sort index first, then task id. 1377 | const diff = a.sortIndex - b.sortIndex; 1378 | return diff !== 0 ? diff : a.id - b.id; 1379 | } 1380 | 1381 | // TODO: Use symbols? 1382 | const ImmediatePriority = 1; 1383 | const UserBlockingPriority = 2; 1384 | const NormalPriority = 3; 1385 | const LowPriority = 4; 1386 | const IdlePriority = 5; 1387 | 1388 | function markTaskErrored(task, ms) {} 1389 | 1390 | /* eslint-disable no-var */ 1391 | // Math.pow(2, 30) - 1 1392 | // 0b111111111111111111111111111111 1393 | 1394 | var maxSigned31BitInt = 1073741823; // Times out immediately 1395 | 1396 | var IMMEDIATE_PRIORITY_TIMEOUT = -1; // Eventually times out 1397 | 1398 | var USER_BLOCKING_PRIORITY = 250; 1399 | var NORMAL_PRIORITY_TIMEOUT = 5000; 1400 | var LOW_PRIORITY_TIMEOUT = 10000; // Never times out 1401 | 1402 | var IDLE_PRIORITY = maxSigned31BitInt; // Tasks are stored on a min heap 1403 | 1404 | var taskQueue = []; 1405 | var timerQueue = []; // Incrementing id counter. Used to maintain insertion order. 1406 | 1407 | var taskIdCounter = 1; // Pausing the scheduler is useful for debugging. 1408 | var currentTask = null; 1409 | var currentPriorityLevel = NormalPriority; // This is set while performing work, to prevent re-entrancy. 1410 | 1411 | var isPerformingWork = false; 1412 | var isHostCallbackScheduled = false; 1413 | var isHostTimeoutScheduled = false; 1414 | 1415 | function advanceTimers(currentTime) { 1416 | // Check for tasks that are no longer delayed and add them to the queue. 1417 | let timer = peek(timerQueue); 1418 | 1419 | while (timer !== null) { 1420 | if (timer.callback === null) { 1421 | // Timer was cancelled. 1422 | pop(timerQueue); 1423 | } else if (timer.startTime <= currentTime) { 1424 | // Timer fired. Transfer to the task queue. 1425 | pop(timerQueue); 1426 | timer.sortIndex = timer.expirationTime; 1427 | push(taskQueue, timer); 1428 | } else { 1429 | // Remaining timers are pending. 1430 | return; 1431 | } 1432 | 1433 | timer = peek(timerQueue); 1434 | } 1435 | } 1436 | 1437 | function handleTimeout(currentTime) { 1438 | isHostTimeoutScheduled = false; 1439 | advanceTimers(currentTime); 1440 | 1441 | if (!isHostCallbackScheduled) { 1442 | if (peek(taskQueue) !== null) { 1443 | isHostCallbackScheduled = true; 1444 | requestHostCallback(flushWork); 1445 | } else { 1446 | const firstTimer = peek(timerQueue); 1447 | 1448 | if (firstTimer !== null) { 1449 | requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime); 1450 | } 1451 | } 1452 | } 1453 | } 1454 | 1455 | function flushWork(hasTimeRemaining, initialTime) { 1456 | isHostCallbackScheduled = false; 1457 | 1458 | if (isHostTimeoutScheduled) { 1459 | // We scheduled a timeout but it's no longer needed. Cancel it. 1460 | isHostTimeoutScheduled = false; 1461 | cancelHostTimeout(); 1462 | } 1463 | 1464 | isPerformingWork = true; 1465 | const previousPriorityLevel = currentPriorityLevel; 1466 | 1467 | try { 1468 | if (enableProfiling) { 1469 | try { 1470 | return workLoop(hasTimeRemaining, initialTime); 1471 | } catch (error) { 1472 | if (currentTask !== null) { 1473 | const currentTime = getCurrentTime(); 1474 | markTaskErrored(currentTask, currentTime); 1475 | currentTask.isQueued = false; 1476 | } 1477 | 1478 | throw error; 1479 | } 1480 | } else { 1481 | // No catch in prod codepath. 1482 | return workLoop(hasTimeRemaining, initialTime); 1483 | } 1484 | } finally { 1485 | currentTask = null; 1486 | currentPriorityLevel = previousPriorityLevel; 1487 | isPerformingWork = false; 1488 | } 1489 | } 1490 | 1491 | function workLoop(hasTimeRemaining, initialTime) { 1492 | let currentTime = initialTime; 1493 | advanceTimers(currentTime); 1494 | currentTask = peek(taskQueue); 1495 | 1496 | while (currentTask !== null && !enableSchedulerDebugging) { 1497 | if ( 1498 | currentTask.expirationTime > currentTime && 1499 | (!hasTimeRemaining || shouldYieldToHost()) 1500 | ) { 1501 | // This currentTask hasn't expired, and we've reached the deadline. 1502 | break; 1503 | } 1504 | 1505 | const callback = currentTask.callback; 1506 | 1507 | if (callback !== null) { 1508 | currentTask.callback = null; 1509 | currentPriorityLevel = currentTask.priorityLevel; 1510 | const didUserCallbackTimeout = 1511 | currentTask.expirationTime <= currentTime; 1512 | const continuationCallback = callback(didUserCallbackTimeout); 1513 | currentTime = getCurrentTime(); 1514 | 1515 | if (typeof continuationCallback === "function") { 1516 | currentTask.callback = continuationCallback; 1517 | } else { 1518 | if (currentTask === peek(taskQueue)) { 1519 | pop(taskQueue); 1520 | } 1521 | } 1522 | 1523 | advanceTimers(currentTime); 1524 | } else { 1525 | pop(taskQueue); 1526 | } 1527 | 1528 | currentTask = peek(taskQueue); 1529 | } // Return whether there's additional work 1530 | 1531 | if (currentTask !== null) { 1532 | return true; 1533 | } else { 1534 | const firstTimer = peek(timerQueue); 1535 | 1536 | if (firstTimer !== null) { 1537 | requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime); 1538 | } 1539 | 1540 | return false; 1541 | } 1542 | } 1543 | 1544 | function unstable_runWithPriority(priorityLevel, eventHandler) { 1545 | switch (priorityLevel) { 1546 | case ImmediatePriority: 1547 | case UserBlockingPriority: 1548 | case NormalPriority: 1549 | case LowPriority: 1550 | case IdlePriority: 1551 | break; 1552 | 1553 | default: 1554 | priorityLevel = NormalPriority; 1555 | } 1556 | 1557 | var previousPriorityLevel = currentPriorityLevel; 1558 | currentPriorityLevel = priorityLevel; 1559 | 1560 | try { 1561 | return eventHandler(); 1562 | } finally { 1563 | currentPriorityLevel = previousPriorityLevel; 1564 | } 1565 | } 1566 | 1567 | function unstable_next(eventHandler) { 1568 | var priorityLevel; 1569 | 1570 | switch (currentPriorityLevel) { 1571 | case ImmediatePriority: 1572 | case UserBlockingPriority: 1573 | case NormalPriority: 1574 | // Shift down to normal priority 1575 | priorityLevel = NormalPriority; 1576 | break; 1577 | 1578 | default: 1579 | // Anything lower than normal priority should remain at the current level. 1580 | priorityLevel = currentPriorityLevel; 1581 | break; 1582 | } 1583 | 1584 | var previousPriorityLevel = currentPriorityLevel; 1585 | currentPriorityLevel = priorityLevel; 1586 | 1587 | try { 1588 | return eventHandler(); 1589 | } finally { 1590 | currentPriorityLevel = previousPriorityLevel; 1591 | } 1592 | } 1593 | 1594 | function unstable_wrapCallback(callback) { 1595 | var parentPriorityLevel = currentPriorityLevel; 1596 | return function () { 1597 | // This is a fork of runWithPriority, inlined for performance. 1598 | var previousPriorityLevel = currentPriorityLevel; 1599 | currentPriorityLevel = parentPriorityLevel; 1600 | 1601 | try { 1602 | return callback.apply(this, arguments); 1603 | } finally { 1604 | currentPriorityLevel = previousPriorityLevel; 1605 | } 1606 | }; 1607 | } 1608 | 1609 | function timeoutForPriorityLevel(priorityLevel) { 1610 | switch (priorityLevel) { 1611 | case ImmediatePriority: 1612 | return IMMEDIATE_PRIORITY_TIMEOUT; 1613 | 1614 | case UserBlockingPriority: 1615 | return USER_BLOCKING_PRIORITY; 1616 | 1617 | case IdlePriority: 1618 | return IDLE_PRIORITY; 1619 | 1620 | case LowPriority: 1621 | return LOW_PRIORITY_TIMEOUT; 1622 | 1623 | case NormalPriority: 1624 | default: 1625 | return NORMAL_PRIORITY_TIMEOUT; 1626 | } 1627 | } 1628 | 1629 | function unstable_scheduleCallback(priorityLevel, callback, options) { 1630 | var currentTime = getCurrentTime(); 1631 | var startTime; 1632 | var timeout; 1633 | 1634 | if (typeof options === "object" && options !== null) { 1635 | var delay = options.delay; 1636 | 1637 | if (typeof delay === "number" && delay > 0) { 1638 | startTime = currentTime + delay; 1639 | } else { 1640 | startTime = currentTime; 1641 | } 1642 | 1643 | timeout = 1644 | typeof options.timeout === "number" 1645 | ? options.timeout 1646 | : timeoutForPriorityLevel(priorityLevel); 1647 | } else { 1648 | timeout = timeoutForPriorityLevel(priorityLevel); 1649 | startTime = currentTime; 1650 | } 1651 | 1652 | var expirationTime = startTime + timeout; 1653 | var newTask = { 1654 | id: taskIdCounter++, 1655 | callback, 1656 | priorityLevel, 1657 | startTime, 1658 | expirationTime, 1659 | sortIndex: -1, 1660 | }; 1661 | 1662 | if (startTime > currentTime) { 1663 | // This is a delayed task. 1664 | newTask.sortIndex = startTime; 1665 | push(timerQueue, newTask); 1666 | 1667 | if (peek(taskQueue) === null && newTask === peek(timerQueue)) { 1668 | // All tasks are delayed, and this is the task with the earliest delay. 1669 | if (isHostTimeoutScheduled) { 1670 | // Cancel an existing timeout. 1671 | cancelHostTimeout(); 1672 | } else { 1673 | isHostTimeoutScheduled = true; 1674 | } // Schedule a timeout. 1675 | 1676 | requestHostTimeout(handleTimeout, startTime - currentTime); 1677 | } 1678 | } else { 1679 | newTask.sortIndex = expirationTime; 1680 | push(taskQueue, newTask); 1681 | // wait until the next time we yield. 1682 | 1683 | if (!isHostCallbackScheduled && !isPerformingWork) { 1684 | isHostCallbackScheduled = true; 1685 | requestHostCallback(flushWork); 1686 | } 1687 | } 1688 | 1689 | return newTask; 1690 | } 1691 | 1692 | function unstable_pauseExecution() {} 1693 | 1694 | function unstable_continueExecution() { 1695 | if (!isHostCallbackScheduled && !isPerformingWork) { 1696 | isHostCallbackScheduled = true; 1697 | requestHostCallback(flushWork); 1698 | } 1699 | } 1700 | 1701 | function unstable_getFirstCallbackNode() { 1702 | return peek(taskQueue); 1703 | } 1704 | 1705 | function unstable_cancelCallback(task) { 1706 | // remove from the queue because you can't remove arbitrary nodes from an 1707 | // array based heap, only the first one.) 1708 | 1709 | task.callback = null; 1710 | } 1711 | 1712 | function unstable_getCurrentPriorityLevel() { 1713 | return currentPriorityLevel; 1714 | } 1715 | 1716 | function unstable_shouldYield() { 1717 | const currentTime = getCurrentTime(); 1718 | advanceTimers(currentTime); 1719 | const firstTask = peek(taskQueue); 1720 | return ( 1721 | (firstTask !== currentTask && 1722 | currentTask !== null && 1723 | firstTask !== null && 1724 | firstTask.callback !== null && 1725 | firstTask.startTime <= currentTime && 1726 | firstTask.expirationTime < currentTask.expirationTime) || 1727 | shouldYieldToHost() 1728 | ); 1729 | } 1730 | 1731 | const unstable_requestPaint = requestPaint; 1732 | const unstable_Profiling = null; 1733 | 1734 | var Scheduler = { 1735 | __proto__: null, 1736 | unstable_ImmediatePriority: ImmediatePriority, 1737 | unstable_UserBlockingPriority: UserBlockingPriority, 1738 | unstable_NormalPriority: NormalPriority, 1739 | unstable_IdlePriority: IdlePriority, 1740 | unstable_LowPriority: LowPriority, 1741 | unstable_runWithPriority: unstable_runWithPriority, 1742 | unstable_next: unstable_next, 1743 | unstable_scheduleCallback: unstable_scheduleCallback, 1744 | unstable_cancelCallback: unstable_cancelCallback, 1745 | unstable_wrapCallback: unstable_wrapCallback, 1746 | unstable_getCurrentPriorityLevel: unstable_getCurrentPriorityLevel, 1747 | unstable_shouldYield: unstable_shouldYield, 1748 | unstable_requestPaint: unstable_requestPaint, 1749 | unstable_continueExecution: unstable_continueExecution, 1750 | unstable_pauseExecution: unstable_pauseExecution, 1751 | unstable_getFirstCallbackNode: unstable_getFirstCallbackNode, 1752 | get unstable_now() { 1753 | return getCurrentTime; 1754 | }, 1755 | get unstable_forceFrameRate() { 1756 | return forceFrameRate; 1757 | }, 1758 | unstable_Profiling: unstable_Profiling, 1759 | }; 1760 | 1761 | let threadIDCounter = 0; // Set of currently traced interactions. 1762 | // Interactions "stack"– 1763 | // Meaning that newly traced interactions are appended to the previously active set. 1764 | // When an interaction goes out of scope, the previous set (if any) is restored. 1765 | 1766 | let interactionsRef = null; // Listener(s) to notify when interactions begin and end. 1767 | 1768 | let subscriberRef = null; 1769 | function unstable_clear(callback) { 1770 | { 1771 | return callback(); 1772 | } 1773 | } 1774 | function unstable_getCurrent() { 1775 | { 1776 | return null; 1777 | } 1778 | } 1779 | function unstable_getThreadID() { 1780 | return ++threadIDCounter; 1781 | } 1782 | function unstable_trace(name, timestamp, callback) { 1783 | { 1784 | return callback(); 1785 | } 1786 | } 1787 | function unstable_wrap(callback) { 1788 | { 1789 | return callback; 1790 | } 1791 | } 1792 | 1793 | function unstable_subscribe(subscriber) {} 1794 | function unstable_unsubscribe(subscriber) {} 1795 | 1796 | var SchedulerTracing = { 1797 | __proto__: null, 1798 | __interactionsRef: interactionsRef, 1799 | __subscriberRef: subscriberRef, 1800 | unstable_clear: unstable_clear, 1801 | unstable_getCurrent: unstable_getCurrent, 1802 | unstable_getThreadID: unstable_getThreadID, 1803 | unstable_trace: unstable_trace, 1804 | unstable_wrap: unstable_wrap, 1805 | unstable_subscribe: unstable_subscribe, 1806 | unstable_unsubscribe: unstable_unsubscribe, 1807 | }; 1808 | 1809 | /** 1810 | * Used by act() to track whether you're inside an act() scope. 1811 | */ 1812 | const IsSomeRendererActing = { 1813 | current: false, 1814 | }; 1815 | 1816 | const ReactSharedInternals = { 1817 | ReactCurrentDispatcher, 1818 | ReactCurrentOwner, 1819 | IsSomeRendererActing, 1820 | ReactCurrentBatchConfig, 1821 | // Used by renderers to avoid bundling object-assign twice in UMD bundles: 1822 | assign: _assign$1, 1823 | }; 1824 | // This avoids introducing a dependency on a new UMD global in a minor update, 1825 | // Since that would be a breaking change (e.g. for all existing CodeSandboxes). 1826 | // This re-export is only required for UMD bundles; 1827 | // CJS bundles use the shared NPM package. 1828 | 1829 | _assign$1(ReactSharedInternals, { 1830 | Scheduler, 1831 | SchedulerTracing, 1832 | }); 1833 | 1834 | const createElement$1 = createElement; 1835 | const cloneElement$1 = cloneElement; 1836 | const createFactory$1 = createFactory; 1837 | const Children = { 1838 | map: mapChildren, 1839 | forEach: forEachChildren, 1840 | count: countChildren, 1841 | toArray, 1842 | only: onlyChild, 1843 | }; 1844 | 1845 | exports.Children = Children; 1846 | exports.Component = Component; 1847 | exports.PureComponent = PureComponent; 1848 | exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactSharedInternals; 1849 | exports.block = block; 1850 | exports.cloneElement = cloneElement$1; 1851 | exports.createContext = createContext; 1852 | exports.createElement = createElement$1; 1853 | exports.createFactory = createFactory$1; 1854 | exports.createMutableSource = createMutableSource; 1855 | exports.createRef = createRef; 1856 | exports.forwardRef = forwardRef; 1857 | exports.isValidElement = isValidElement; 1858 | exports.lazy = lazy; 1859 | exports.memo = memo; 1860 | exports.unstable_useOpaqueIdentifier = useOpaqueIdentifier; 1861 | exports.unstable_withSuspenseConfig = withSuspenseConfig; 1862 | exports.useCallback = useCallback; 1863 | exports.useContext = useContext; 1864 | exports.useDebugValue = useDebugValue; 1865 | exports.useDeferredValue = useDeferredValue; 1866 | exports.useEffect = useEffect; 1867 | exports.useImperativeHandle = useImperativeHandle; 1868 | exports.useLayoutEffect = useLayoutEffect; 1869 | exports.useMemo = useMemo; 1870 | exports.useMutableSource = useMutableSource; 1871 | exports.useReducer = useReducer; 1872 | exports.useRef = useRef; 1873 | exports.useState = useState; 1874 | exports.useTransition = useTransition; 1875 | exports.version = ReactVersion; 1876 | }); 1877 | --------------------------------------------------------------------------------