The React Source Code: a Beginner’s Walkthrough I

Eric Churchill
35 min readApr 25, 2017

--

courtesy of: https://github.com/facebook/react/blob/master/docs/img/logo_og.png

Ever been stuck in a video game? Do you remember reaching for that strategy guide while battling Shadow Link in the Water Temple? In the spirit of a video game walkthrough, this is a journey through the React Core source code so that beginners may achieve a deeper understanding of the tools they use. Many developers understand the what and how but miss the depth that conveys the why of React.

Furthermore, we write better code by reading good code. I know firsthand how intimidating the broad-ranging scope of the React source can be. It is my hope that by building a line-by-line walkthrough of the React Core source, its patterns and nuances will emerge to beginner developers. This knowledge should lead to a better understanding of the code developers write, encourage informed contributions to the community, and ultimately demystify open source projects.

I will try to make no assumptions about the reader, other than a familiarity with JavaScript and experience with React. If there are any concepts that need further clarification, let me know and I will try to elaborate. Also, this is as much a journey for me as it will (hopefully) be for many of you, so I am certain to confound some important facts or concepts. Please feel free to kindly correct any of these misunderstandings and I will be happy to edit this or any future pieces.

What is React?

This section wasn’t originally included, but is actually the result of a conversation with my dad. When he heard I was writing an article on React Core, he said, ‘What’s that? You’re writing a reactor? Does the government know about that?’

We laugh but the truth is, we as a community take certain high-level assumptions for granted. So here it is — for all the dads in the world who wonder what their sons and daughters are up to: React is an extremely popular web framework (i.e. used to build web applications) that was open sourced by Facebook. Unfortunately it has nothing to do with nuclear fusion or submarines.

Into the Code

package.json

When I first jump into a library I begin at the package.json, which is often located in the project’s root directory. The package.json’s main field points to the project’s entry point and is where your main module’s exports object is returned. In other words, when a module is ‘required in’, this is the point where the API is imported from. If we look for the main field in the package.json located at React’s root, however, we find the main field is strangely absent. We can gain insight as to why this is the case from the React Codebase overview, here. There are two important things to note from this source. The first is the React repository on GitHub is a monorepo (i.e. React and all its related projects are kept in a single repository), the second is

[t]he npm metadata such as package.json files is located in the packages top-level folder.

Cool, the packages directory might be a good place to explore next. Before moving on, however, you may be wondering why package.json exists at the root, if it doesn’t actually provide an entry point. One subtle clue to this question lies in the package.json’s dependencies. The only dependencies that the package relies on are devDependencies (in other words, no direct dependencies). Furthermore, the npm scripts in the scripts field are all development scripts, giving access to useful CLI commands like npm run build, npm t, npm run flow, etc. Therefore, it is reasonable to assume this package.json exists to set up the development environment.

Let’s now move on to the packages directory at the root. We’re interested in the core React module for now so navigate to react/packages/react/package.json. You should notice that this package.json contains a main field that points to index.js as well as a few dependencies. If we navigate to react.js (credit to Hozefa for his corrections to this paragraph) we might be surprised to find something that looks like this:

react.js

That’s right — only three lines of code, one of which is 'use strict';, the other appears to be exporting a non-existent file in a non-existent directory. Once again, the reasons for this mysterious file are answered by the Codebase Overview:

When we compile React for npm, a script copies all the modules into a single flat directory called lib and prepends all require() paths with ./

A Brief Note on Haste

Navigating to https://unpkg.com/react@15.4.1/lib/, we can see that there is indeed a file named React.js. But from where did this file originate? The answer lies in Facebook’s custom module system, Haste. Haste was developed to be convenient for projects with large codebases, so that files can be moved to different locations within a project without having to worry about specifying new relative file paths. The key distinction between Haste and Common.js, however, is that all filenames within a Haste project must be globally unique. When a new file is created in a Facebook project, Facebook requires that file include a license header. Within that header appears a line like * @providesModule React where the name following @providesModule should match the name of the newly created file. So, in this case @providesModule React imports the React module. This is how modules are created in Haste.

React.js

Having globally unique filenames also makes finding files incredibly simple on GitHub and most text editors. Pressing t anywhere in React’s GitHub repository, we can type react.js to search for the core React module. Indeed, we find a file named React.js located in src/isomorphic as the third hit from the top.

That’s the one!

Navigating to src/isomorphic/React.js we finally see a Javascript file that’s more than three lines of code!

The Core Module

React.js (cont)

The Facebook License Header

Lines 1–10: As I mentioned before, each source file in React requires a license header. These lines contain information about the license and are used by Haste (line 9) to uniquely identify the module.

React.js: lines 14–21

Lines 14–21: These lines leverage Haste to import React.js’s module dependencies. The modules required in here will ultimately help comprise React’s top-level public API. We’ll dive into their code in a bit.

React.js: lines 23–24

Lines 23–24: These lines require in two more modules that do not comprise the top-level API but are used within the React.js file itself. One interesting note about the warning module: if you search the repository for it, you might be surprised to find that it doesn’t exist in the React repository. Instead, it is located in Facebook’s fbjs repository here, which is included as a dependency in React’s package.json. If you’re curious, you can learn more about why it’s kept in another repository here.

React.js: lines 26–28

Lines 26–28: The code here serves two purposes in my mind. 1) It creates a series of utility variables that are more expressive and less verbose than continuously accessing properties on the ReactElement object and 2) the newly recreated variables can safely be reassigned in the development environment, without overwriting the modules’ functionalities.

React.js: lines 30–35

Lines 30–35: One convention used frequently throughout the React source code is the use of __DEV__ pseudo-global variable. You can read more about it here, but the essential concept to understand is that it guards development-only blocks of code — in other words, it creates blocks of code that will only execute in the developer environment and will not execute in production environments. In this case, if React is being executed in the development environment, require in ReactElementValidator and overwrite the variables assigned on lines 26–28 with their ReactElementValidator counterparts.

React.js: line 37

Line 37: This might seem like an unnecessary line at first, but its purpose emerges on closer inspection. In JavaScript functions are first class (i.e. are just like any other value) and can be assigned to variables. Here, __spread is simply a variable that holds a reference to the same function as Object.assign, which can be learned about here. This is important because as we’ll see in a moment, React.__spread has been deprecated and this ensures that it now points to the same function as Object.assign. If this single line of code still looks mysterious, try running the following code in a REPL and consider the behavior that might generate the following output:

__spread
React.js: lines 39–52

Lines 39–52: Once again, we encounter an if block whose conditional checks for the __DEV__ execution environment. Once again, this is React’s way of blocking development code in the production environment. Clearly, the functionality of this block depends heavily on the warning function required earlier in the file. Therefore, in order to understand the intention of this code, we must first understand warning.js.

A Detour through warning.js

I’ve decided to include this section for the sake of thoroughness and because its functionality is leveraged liberally throughout the React codebase. However, the module’s title is self-explanatory and the code relatively trivial, so feel free to skip this section if you’d like.

The warning module begins by importing emptyFunction from the emptyFunction.js module. The important line here that is ultimately used by our warning module is line 24, which looks like:

emptyFunction.js: line 24

If we strip away the FlowType from this function we’re left with:

emptyFunction stripped of its FlowTypes

This is an idiomatic way of stripping a function of its arguments and then simply returning an anonymous function. This is important because we don’t want to be warned while executing in non-__DEV__ environments. In this case, however, we know that the program’s execution environment is the development environment (i.e. __DEV__ evaluates to true), so warning will be overwritten on line 40.

warning.js: lines 40–50

This warning function’s purpose is to communicate errors in a constructive way while in the __DEV__ environment. It accepts a condition, format, and ...args as its parameters. Following line 40, the warning function will check whether format — which is the error message — is defined. If it’s not, an exception will be raised stating, ‘`warning(condition, format, …args)` requires a warning message argument’. The truthiness of thecondition argument is then checked and, if false, invokes printWarning on line 26. printWarning will then check if the console exists and, if so, it will invoke console.error on message; otherwise, it will attemptthrow new Error(message);. Finally, warning is exported.

Back to React.js

React.js: lines 39–52 (cont.)

Now understanding the what, why, and how of warning.js we can continue stepping through the code. Line 40 of React.js represents the beginning of a pattern common throughout the React codebase where a variable called warned is set to false. On the next line, __spread is reassigned to a function that invokes warning passing in the warned flag and the message referred to here as format. This ensures that in production environments React.__spread aliases Object.assign, while in __DEV__ environments invoking React.__spread warns the user that __spread is now deprecated and its use should be avoided. Next, the warned flag is flipped to true, so that warning only logs or throws an error the first time React.__spread is invoked. Finally, Object.assign is invoked on arguments and the resulting object is returned. Moral of the story: React.__spread should be avoided when possible and Object.assign used in its place.

React.js: line 54–71

Lines 54–71: This is where React’s top-level public modern API is defined. For example, React.Component and React.PureComponent are exposed here as Component: ReactComponent and PureComponent: ReactPureComponent, respectively.

React.js: lines 58–64

Lines 58–64: This is where React.Children's API is defined, which includes the following methods: map, forEach, count, toArray, and only.

React.js: lines 66–67

Lines 66–67: These lines define the React.Component and React.PureComponent API.

React.js: lines 69–71

Lines 69–71: This is where the React.createElement, React.cloneElement, and React.isValidElement are defined.

React.js: line 75–90

Lines 75–90: This is where React’s top-level public classic API is defined. We’re not going to spend much time with these, but some of them are worth exploring on your own.

React.js: lines 75–81

Lines 75–81: This is where React.PropTypes, React.createClass, React.createFactory, and React.createMixin are defined and exposed as public API.

React.js: line 85

Line 85: This line defines React.DOM, which interestingly is not DOM specific, but actually a series of isomorphic helpers that return DOM strings.

React.js: line 87

Line 87: Defines React.version, which contains the current version of React.

React.js: line 90

Line 90: Defines React.__spread. Once again, this method is deprecated and should be avoided, in favor of Object.assign when possible.

React.js: line 93

Line 93: Finally, the React module is exported on the last line of React.js.

A Deeper Dive

We’ve now defined React’s public API. At this point the path diverges in a number of directions. We could explore React.PureComponent, React.Component, or React.Children, but I’ve decided to follow the path of least resistance and prioritize our exploration based on ease. Therefore, we’ll begin with the most straight-forward module, React.Children.onlyChild, followed by the React.Component and React.PureComponent modules, and finish by circling back to the more complex constituents of React.Children.

The React.Children.onlyChild Module

The onlyChild module is a single function exported by react/src/isomorphic/children/onlyChild.js. This module requires only two other modules as its dependencies: invariant and ReactElement. The invariant module is very similar in functionality to the warning module we investigated earlier where if a condition does not evaluate to true, an exception will be raised. The difference being the exception raised by invariant will be raised in both __DEV__ and production environments alike. The other module, ReactElement, is located in react/src/isomorphic/classic/element/ReactElement.js and consists of 386 lines of code. For the purposes of React.Children.onlyChild, however, we’re only interested in lines 372–384. The important thing to understand about ReactElement.isValidElement is that it accepts a React component and validates that it is indeed a React component by:

It first checks the component’s type to determine if it’s an object and does not equal null and the component has the $$typeof property that’s set to the REACT_ELEMENT_TYPE Symbol. If all conditions are true, ReactElement.isValidElement returns true. Therefore, if onlyChild is not passed a single valid React component, it will throw an invariance exception; otherwise, it will return the component.

I like to evangelize testing when I can because I believe a lot can be gained by reading through a module’s tests. Tests guard against regressions and help define the contract by which their covered functionality must abide. Therefore, I’d like to spend a moment on React.Children.onlyChild’s test suite.

A Note on Testing in React

Testing in React is done via Facebook’s Jest testing framework. A file’s tests can be found in an intuitive and predictable location. For each tested file, there will be a colocated directory called __tests__that will hold the test suites for all files in the same directory. For example, because the onlyChild module is located in the src/isomorphic/children directory, its tests will be found in the src/isomorphic/children/__tests__ directory. This makes finding a file’s tests manageable and makes remembering to move a file’s tests simple.

Back to onlyChild

Now that we know where onlyChild’s tests are located, we can better understand its code by reading them. Go ahead and navigate to src/isomorphic/children/__tests__/onlyChild-test.js. I won’t spend too much time on the implementation of the tests here, but know that each test passes and sheds light on how and why onlyChild might be used. Immediately we learn that onlyChild:

  • should fail when passed two children
  • should fail when passed nully values
  • should fail when key/value objects
  • should not fail when passed interpolated single child
  • should return the only child

All of that information is gathered in a matter of seconds by simply glimpsing onlyChild’s validation suite.

React.Component

Next, I’d like to look to pivot and walk through React.Component. Don’t worry, we’ll return to investigate the remainder of the React.Children code in a bit. We begin with React.Component and not React.PureComponent because React.PureComponent is built on top of React.Component, so having an understanding of the former facilitates the latter.

React.Component can be found in the project directory at react/src/isomorphic/modern/class/ReactComponent.js. We’ll skip the license headers from here on out because their format is unchanged throughout the repository.

ReactComponent.js: line 14

Line 14: Requires the ReactNoopUpdateQueue module. This module is essentially a dummy replacement for when a ReactComponent’s updater parameter does not exist. Its functionality is minimal and is primarily useful for warning the user against updating unmounted components.

ReactComponent.js: lines 16–19

Lines 16–19: These lines require in utility modules from the src/shared/utils directory as well as from the fbjs repository. Once again, the invariant and warning modules are required as well as a module called emptyObject which comes from the fbjs repository. This module creates an empty object that is immediately frozen with Object.freeze in the development environment and that object is then exported.

A Detour through canDefineProperty Module

The last module to be required in is the canDefineProperty module from react/src/shared/utils/canDefineProperty.js. This is a very interesting module that leverages theObject.defineProperty method. If you’re unfamiliar with Object.defineProperty, the MDN documentation does an excellent job explaining its purpose:

This method allows precise addition to or modification of a property on an object. Normal property addition through assignment creates properties which show up during property enumeration (for...in loop or Object.keys method), whose values may be changed, and which may be deleted. This method allows these extra details to be changed from their defaults. By default, values added using Object.defineProperty() are immutable.

In other words, properties created by Object.defineProperty are both unchangeable and non-enumerable by default. If we look at the canDefineProperty.js in the React repository, we see how this is used.

canDefineProperty.js: lines 15–24

Lines 15–24: A flag called canDefineProperty is declared and initialized to the Boolean value of false. Then, if React is executing in the __DEV__ environment, it will attempt to invoke Object.defineProperty on a dummy object. You might be wondering why this step is necessary if we’re not doing anything with the dummy object. Well, if we look at the try’s corresponding catch statement, we’ll see a comment indicating that Internet Explorer will fail on Object.canDefineProperty. This means line 19 will raise an exception before the Boolean is flipped and the exception will be swallowed by the catch statement. Finally, canDefineProperty is exported.

Back to ReactComponent.js

Now that we know what the canDefineProperty module exports, let’s return to ReactComponent.js.

ReactComponent.js: lines 24–31

Lines 24–31: These are the lines that define the base class which is extend to build React components! The function on line 24 is a pseudoclassical constructor function that creates React Component instances. For those of you unfamiliar with the pseudoclassical object instantiation pattern, you can read more about it here. As we can see, a component’s props, context, and updater are set here, based on the parameters passed into the constructor at run time. If updater is unspecified, it will be set to the ReactNoopUpdateQueue module required above. The component’s refs are set to the empty object exported here.

When we write, for example:

class button extends React.Component {
// some code
// ..
// ..
}

this is the function that is actually being extended!

ReactComponent.js: line 33

Line 33: Sets the isReactComponent property on ReactComponent.prototype and assigns an empty object to it.

ReactComponent.js: lines 60–69

Lines 60–69: These lines define the setState method on the ReactComponent.prototype. The first thing that setState does is to guard against the invariance where the partialState parameter is neither of type ‘object’ nor ‘function’ nor null. Recall that partialState can not only represent what new information should be merged into state, but how it can be merged as well. Sophia Shomaker has a great primer on passing callbacks to setState here. If partialState is none of those types, it will raise the invariance; otherwise the intention to set a new state will be passed along to the React reconciler via the enqueueSetState method on the updater.

ReactComponent.js: lines 85–87

Lines 85–87: These lines define the forceUpdate method on the ReactComponent.prototype. This function merely tells the reconciler that something has changed and the state should be updated by invoking the enqueueForceUpdate method on the component’s updater property. Recall that the updater is injected at runtime and the default updater property might not possess enqueueForceUpdate!

ReactComponent.js: lines 94–127

Lines 94–127: These lines are used in the development environment to warn developers against utilizing now-deprecated APIs. Specifically, if a developer uses the isMounted (line 96) or the replaceState (line 101) APIs from the deprecatedAPIs object, a warning will be raised. The defineDeprecationWarning on line 107, is why the canDefineProperty module required earlier is necessary. It adds deprecated API method names to the ReactComponent.prototype as non-enumerable properties, so when they’re accessed a warning will be raised. Recall, when properties are added to objects with Object.defineProperty, they’re essentially invisible until they’re accessed. Here the deprecated APIs are being ‘hidden’. If they are accessed, however, the warning on their getters will be raised. Finally, the for…in loop on line 122 programmatically adds deprecated APIs to the ReactComponent.prototype. Interestingly, APIs deprecated in the future can simply be added to the deprecatedAPIs object on line 95, and the appropriate warning will be raised when accessed in the __DEV__ environment.

ReactComponent.js: line 129

Line 129: The ReactComponent module is exported.

That’s it for the ReactComponent module’s API. Now that we’ve covered the ReactComponent module, we can investigate ReactPureComponent, which inherits from it.

ReactPureComponent

A close reading of ReactPureComponent.js which can be found in the react/src/isomorphic/modern/class directory, exposes stark similarities between it and its ReactComponent.js counterpart. In fact, the ReactComponent module is imported into the ReactPureComponent module on line 14, along with the ReactNoopUpdateQueue and emptyObject modules — both of which are module dependencies to ReactComponent. We’ll skip these lines, because their functionalities can be referenced above.

ReactPureComponent.js: lines 22–30

Lines 22–30: This function should look very familiar. It is the exact same function that appears in on line 24 of ReactComponent.js, only called ReactPureComponent. Indeed, line 23 is a comment admitting that the code was duplicated from the ReactComponent module.

ReactPureComponent.js: lines 32–37

Lines 32–37: These lines might seem unfamiliar at first, especially if you’re unfamiliar with delegate prototypes in Javascript. If you would like to know more about inheritance and the prototypal chain, MDN has an excellent article that you can read here. There is a small amount of indirection here with the ComponentDummy pseudoclass that I will do my best to break down. The ComponentDummy constructor is defined on line 32 and will instantiate an empty object that delegates to the ComponentDummy.prototype. Next, ComponentDummy’s prototype is overwritten by ReactComponent’s protoype on line 33. Then, ReactPureComponent’s prototype is overwritten by an instance of ComponentDummy, which is just an empty object whose __proto__ points to ReactComponent’s prototype on line 34. This allows the behavior defined on the ReactComponent.prototype to delegate to the ReactPureComponent.prototype without having any knowledge of the existence of ReactPureComponent. If that doesn’t make sense, take a look at the following snippet and try to understand what’s going on in it.

Next, ReactPureComponent is reassigned to the constructor property on the prototype on line 35. This is necessary because the constructor was overwritten by the new ComponentDummy instance earlier. Line 37 may look a little odd at first, considering it seems like the constructor we just set on the ReactPureComponent.prototype is being overwritten. Fortunately it’s not. The reason for this is Object.assign's behavior. It

only copies enumerable and own properties

While the constructor property on the ReactPureComponent.prototype is enumerable because we defined it literally, the constructor on ReactComponent.prototype is not! Finally, we assign a property to the ReactPureComponent.prototype called isPureReactComponent that holds the Boolean value of true. Lastly, we export the ReactPureComponent.

ReactPureComponent-test.js: lines 23–64

Before moving on, take a look at ReactPureComponent’s tests colocated at react/src/isomorphic/modern/class/__tests__/ReactPureComponent-test.js. Looking at the first test you immediately see that our ReactPureComponent ‘should render’. I want to take a moment to step through this code, because I believe it’s valuable to understand how the test actually leverages ReactPureComponent. On line 24, the variable renders is declared and initialized to the value 0. This variable will be a counter that holds the number of times our component is rendered. On the next line, Component extends React.PureComponent and its behavior are defined. On line 28, the component’s state is set to { type: 'mushrooms' }. On lines 30–32 the component’s render method is defined. When this render method is invoked, two things happen: 1) the renders variable on line 24 increments as a side-effect and 2) the interpolated this.props.text[0] is wrapped in a div tag. On line 36, a container variable is declared and initialized to an empty HTML div element. On line 45 the text variable is initialized to the array literal ['porcini']. On the next line the Component is rendered to the empty div element and the text is passed in as the component’s props. On line 46, container.textContent is asserted to be the string ‘porcini’. Furthermore, because the component is rendered, the renders variable is asserted to have been incremented 1. Lines 45–48 demonstrate similar behavior. Scrutinizing lines 50–53, clarifies a behavior that can be confusing to beginners of React. Because React component’s only do a shallow comparison of its props, Component will not be rerendered. In other words, because the array that wraps the string is the same — even though the string itself has been updated — the Component will not be rerendered. This is why container.textContent is still asserted to be the string ‘morel’ and the value of renders is expected to be 2. Similarly, on lines 56–58 the setState method is invoked on the Component instance passing in the same state as before. When a partial state that is already present in state is attempted to be merged into state, the component does not rerender. This explains why both container.textContent and renders are unchanged. Finally, in lines 61–63 a new partial state is merge, causing state to update so the component rerenders. Now container.textContent is expected to be the string ‘portebello’ which was set on line 50 and renders is expected to be 3. We also learn that the ReactPureComponent ‘should override shouldComponentUpdate.’ Basically this means that when shouldComponentUpdate returns true, the component’s normal behavior will be overridden by shouldComponentUpdate and the component will always rerender when told to. Lastly, we expect that ReactPureComponent ‘extends React.Component’. Indeed, we see on lines 87–88 that this which is our instance of ReactPureComponent is both an instance of ReactPureComponent and ReactComponent.

ReactElement.js

You’ve just walked through the interface used to build the React components that will ultimately represent the browser’s DOM. You might be wondering, how those classes and their instances are translated into nodes on the so-called virtual DOM. If you’ve never seen a React Element before, they look like this:

A React Element

which represents:

<div>
<span></span>
</div>

in the traditional DOM. The short answer is these React Elements are created in the ReactElement module. ReactElement.js is located in the react/src/isomorphic/classic/element directory and is exposed in the React API as React.createElement, React.createFactory, and React.cloneElement.

ReactElement.js: line 14

Line 14: This line imports the ReactCurrentOwner module which is simply an object whose only property, current, holds the value null, which can be of the FlowTypes null, ReactInstance, or Fiber. If you’re unfamiliar with FlowTypes, you can read more about their API here.

ReactElement.js: lines 16–18

Lines 16–18: These lines require in the familiar modules, warning and canDefineProperty. If you need to be reminded of their functionality, look above. The hasOwnProperty variable is set to Object.prototype.hasOwnProperty and prevents making a prototype jump to the Object.prototype.

ReactElement.js: line 20

Line 20: This line requires in the REACT_ELEMENT_TYPE module. If you navigate to this file, its code might appear complicated at first. All that it’s really doing, however is checking the environment for the presence of the Symbol factory function and then invoking the Symbol constructor on the string ‘react.element’ returns a symbol object. Its code is found here.

ReactElementSymbol module

If you’re wondering what Symbol.for(‘react.element’) does here, it’s checking for the presence of the symbol with the key ‘react.element’. If it finds it, it returns that instance of symbol. Otherwise it creates a new symbol instance with the ‘react.element’ key and returns it. More can be read here.

ReactElement.js: lines 22–27

Lines 22–27: These lines declare the variable RESERVED_PROPS and assign to it an object literal with the properties: key, ref, __self, and __source all set to the Boolean value true. As we’ll soon see, these properties on the RESERVED_PROPS object refer to properties that cannot be overwritten or manually assigned on React Elements.

ReactElement.js: lines 31–41

Lines 31–41: On the first line, hasValidRef checks whether the execution environment. If it is set to __DEV__, the function then checks if the ref property is defined on the config parameter. On line 34, the variable getter is assigned to Object.getOwnPropertyDescriptor(config, 'ref').get. If you are not familiar with getOwnPropertyDescriptor, you can read about it here. Essentially, it returns an object whose properties describe various non-obvious aspects of the property passed to it. One of these properties is the get property, which is why the line ends with .get. Therefore, this line is looking up the property’s getter and assigning it to the getter variable. Next, if the getter variable is defined and the getter.isReactWarning flag is set to true, which you’ll see in a moment, hasValidRef returns false. In other words, if the ref property on the config object is not valid. Lastly, if not in the __DEV__ environment or in the __DEV__ environment but didn’t early return, the hasValidRef returns a Boolean value that returns true if config.ref is not undefined.

ReactElement.js: lines 43–53

Lines 43–53: The hasValidKey function behaves very similarly to its hasValidRef function. The only difference is that it checks for the key property rather than ref.

ReactElement.js: lines 55–74

Lines 55–74: The defineKeyPropWarningGetter begins by defining a new function called warnAboutAccessingKey. The purpose of this function is to check whether the variable specialPropKeyWarningShown in the parent scope remains undefined. If it is, it will set the specialPropKeyWarningShow flag to the Boolean value of true and then raise a warning stating that ‘key is not a prop…’. The flag is necessary to avoid raising multiple warnings for the same event. Next, the property isReactWarning is defined on the function and set to the Boolean value false. Finally, key is added as a non-enumerable property to props whose getter function is warnAboutAccessingKey function that was just defined. Basically, this function is reminding you that key is not a property on the props object. If it is accessed in production environments, it will be undefined. In __DEV__ environments, however, it will raise this warning.

ReactElement.js: line 76–95

Lines 76–95: The function defineRefPropWarningGetter defined here is very similar in functionality to defineKeyPropWarningGetter. The difference is that it adds a non-enumerable warning getter to the ref property on the props object.

ReactElement.js: line 117–177

Lines 117–177: These lines define the function ReactElement. This will ultimately be exported and used in many of the methods on the top-level API. The function begins by defining an object and assigning it to the element variable. This object holds the $$typeof property which is what React uses internally to determine if an object is a bona fide React component. Next, the built-in properties that component contains are added to the object: type, key, ref, and props. Lastly, the _owner property is defined on the ReactElement instance, which contains a reference to its parent (creating) component. You might be wondering why these properties are defined literally, rather than programatically. The reason is discussed in depth here, but basically defining the properties this way is an optimization. Next, the ReactElement function checks if it’s executing in the __DEV__ environment. It it is, React will create a property _store on element that is initialized to an empty object literal. Line 143 ensures that Object.defineProperty exists in the environment. If it does, a series of non-enumerable properties are defined on the element.store object using Object.defineProperty. Lastly, if Object.freeze exists in the execution environment, the element object is frozen and the resulting object is returned.

ReactElement.js: lines 183–265

Lines 183–265: These lines define the ReactElement.createElement function. If that name sounds familiar, it’s because React components written in the JSX language transpile to React.createElement. This method is actually stored on the ReactElement function.

ReactElement.js: lines 184–192

Lines 184–192: These lines declare the variables that comprise the Reserved namespace in React component’s. They are each initialized to null.

ReactElement.js: line 194

Line 194: This line checks if the config parameter is not a nully value.

ReactElement.js: line 195–200

Lines 195–200: If config is not null, it executes the hasValidRef and hasValidKey functions defined above on the config parameter. If they exist and are valid, the config.ref and config.key are assigned to their respective variables.

ReactElement.js: line 202

Line 202: If config.__self is undefined, assign null to the self variable. Otherwise, assign self the value stored at config.__self.

ReactElement.js: line 203

Line 203: Same as line 202, but with source variable and property.

ReactElement.js: lines 205–210

Lines 205–210: These lines simply add the remaining enumerable properties on the config object to the props object. It is important to note here, however, that properties on the config object cannot overwrite properties defined on the RESERVED_PROPS object because of line 208.

Line 215: This line takes measure of the number of children arguments that are passed into the React.CreateElement function. It accomplishes this by subtracting the first two parameters from the total length of the arguments array-like object. For example, if the number of children arguments is (arbitrarily) 4, the total number of arguments will be 6 (4 + 2).

ReactElement.js: line 216–218

Line 216–218: If the number of children arguments is equal to 1, the children parameter is assigned to props.children.

ReactElement.js: lines 218–229

Lines 218–229: If the number of children arguments is greater than one, they will must be stored in an array. A new array is initialized and assigned to the childArray variable. The for loop iterates through the arguments array-like object and pushes the children parameters to the childArray. Next, if the function executes in the __DEV__ environment, the childArray array is frozen. Lastly, the childArray variable is assigned to props.children.

ReactElement.js: lines 232–239

Lines 232–239: These lines check to see if the type parameter is defined and if that parameter has the defaultProps property. If both conditions are true, the variable defaultProps is initialized to type.defaultProps. Next, a for... in loop iterates through the properties on type.defaultProps, and if the property’s value is not undefined, the property and its value are copied into the props object.

ReactElement.js: lines 240–255

Lines 240–255: Next, if the function is executing in the __DEV__ environment, React checks iff key and ref are defined. If they are, another if statement checks if the type of props.$$typeof is equal to the string ‘undefined’ or not equal to REACT_ELEMENT_TYPE. If both are true, a displayName variable is initialized which will be passed into the defineKeyPropWarningGetter and defineRefPropWarningGetter functions defined earlier.

ReactElement.js: lines 256–264

Lines 256–264: Lastly, ReactElement is invoked on the variables defined above and the resulting object returned!

ReactElement.js: lines 271–280

Lines 271–280: In these lines another piece of the top-level API functionality is defined, ReactElement.CreateFactory. On line 272, it defines a function called factory, binding the functions this value to null, as well as binding the type parameter. Then, on line 278 the type parameter is exposed directly on the function as a property so that it can be easily accessed on elements. Finally the factory function is returned.

ReactElement.js: lines 282–294

Lines 282–294: The function defined in these lines is pretty straight-forward. It accepts an existing element and a new key as its parameters. It then invokes ReactElement on properties taken from oldElement (the existing element), also passing in the newKey parameter, and returns the newElement object.

ReactElement.js: lines 300–369

Lines 300–369: If these lines look familiar it’s because they very closely mirror the functionality defined in React.CreateElement. The primary difference is that ReactElement.CloneElement accepts an element rather than a type as its first argument and essentially creates a new element from its input.

ReactElement.js: line 304

Line 304: In this line element.props is copied into the newly declared variable props. The reason Object.assign must be used is because objects are passed by reference. If we simply assigned props to be element.props it would hold the exact same object. Therefore, any changes in one would be reflected in the other. Considering the purpose of ReactElement.CreateElement is to clone a new element from a pre-existing one, this behavior is undesirable. This is why a new object is created and all of its properties are copied over from the element.props.

ReactElement.js: 307–317

Lines 307–317: Once again reserved names are extracted and explicitly defined.

ReactElement.js: lines 319–345

Lines 319–345: So far the new element’s variables have all been copied over from the source element. In these lines, however, ReactElement.CloneElement checks if its config parameter does not evaluate to a nully value. If it doesn't, hasValidRef and hasValidKey are invoked on the config object. If the result of that invocation is true, the variables ref, key, and owner are overwritten. In the remaining lines, the remainder of the props that are neither undefined nor properties on the RESERVED_PROPS object are copied over to the new element.

ReactElement.js: lines 349–358

Lines 349–358: In the same way the children arguments are copied over to the new element above, the children arguments are copied from the source to the target element here.

ReactElement.js: lines 360–368

Lines 360–368: Finally, ReactElement is invoked on the newly defined variables in these lines and the resulting object returned.

ReactElement.js: lines 378–384

Lines 378–384: These lines were already discussed. For an explanation of ReactElement.isValidElement's functionality, see above.

ReactChildren.js

Finally, we arrive at the last piece of the React Core API. This part has been saved for last because it is the most confusing and requires a relatively deep understanding of the Javascript language and patterns. I will try to explain these concepts to the best of my abilities.

The main module is located in the React repository at react/src/isomorphic/children/ReactChildren.js.

ReactChildren.js: lines 14–18

Lines 14–18: These lines import the ReactChildren module’s dependencies. The ReactElement and emptyFunction modules should look familiar at this point, but we’ll investigate the PooledClass and traverseAllChildren modules as they are encountered.

ReactChildren.js: lines 20–21

Lines 20–21: If you peek into the PooledClass module, you’ll notice a number of methods on the exported object. Each of those methods achieves approximately the same goal but have different arities. In lines 20–21, we declare and initialize twoArgumentPooler and fourArgumentPooler to be PooledClass.twoArgumentPooler and PooledClass.fourArgumentPooler from the PooledClass module, respectively.

ReactChildren.js: lines 24–27

Lines 24–27: These lines accept a user-provided key and match for escape characters and replace them with the string ‘$&/’.

Lines 38–42: These lines define a class constructor function that accepts two parameters: forEachFunction, which will wind up representing the callback function passed into ReactChildren.forEach, and forEachContext, which will be discussed when we investigate the traverseAllChildren module. The parameters are then passed to the instance’s func and context properties while the count property is assigned to the number 0.

ReactChildren.js: lines 43–47

Lines 43–47: These lines define a destructor method on the ForEachBookKeeping function. This method effectively resets the properties on the instance created by the class constructor by reassigning the instance’s values to null and 0.

ReactChildren.js: line 48

Line 48: This line leverages the functionality of the PooledClass module’s addPoolingTo method. In order to better understand this function’s purpose and behavior, let’s take a detour through the PooledClass module.

Detour Part I: The PooledClass Module

PooledClass.js: lines 94–111

Lines 94–111: If you don’t have any experience with Flowtype, the code in these lines might look foreign. The <T> on line 95 is a generic type declaration that will replace subsequent Ts. The addPoolingTo function accepts two parameters: CopyConstructor, which should be a generic Class constructor function, and a pooler parameter, which should be of the Pooler type which can be of type any, as declared on line 83. Lines 97–100 declare the type of the function’s return value, which should be of of type Class constructor with the getPooled and release methods, that return the generic type T and void (undefined), respectively. Lines 101–110 actually define the body of the addPoolingTo function. On line 103, the variable newKlass is assigned to the parameter CopyConstructor. Next, the instancePool property is added to newKlass and initialized to an empty array literal. Next, the getPooled property is added to NewKlass and set to the pooler parameter. Lastly, the release property is added to the NewKlass constructor and is set to the standardReleaser function defined above. The NewKlass constructor is then returned. Essentially, PooledClass.addPoolingTo extends a class constructor with a pooler. In the case of React.Children.forEach, the CopyConstructor parameter being passed in is the ForEachBookKeeping function, while the pooler parameter is the twoArgumentPooler function.

PooledClass.js: line 35–44

Lines 35–44: The twoArgumentPooler function accepts two arguments, a1 and a2. So far we’ve seen the this keyword used to build class instances in the pseudoclassical instantiation pattern. On the next line, however, we see the this keyword used in a different context. Here, this refers to the object left of the calldot — in this case, ForEachBookKeeping. Recall that the NewKlass parameter equals ForEachBookKeeping and the pooler parameter equals twoArgumentPooler when invoked like ForEachBookKeeping.getPooled. Lines 37–41 check whether Klass.instancePool.length is not equal to 0, and if not, pops the last element from the Klass.instancePool array and assigns that element to the newly declared variable instance. It then invokes the Klass function on the a1 and a2 arguments, setting the value of this to the newly popped instance object. If the Klass.instancePool array is empty, on the other hand, the twoArgumentPooler leverages the new keyword to invoke Klass on a1 and a2 and returns the new Klass instance.

Back to ReactChildren

ReactChildren.js: lines 50–53

Lines 50–53: The function defined in these lines is a little difficult to understand without the appropriate context. Nevertheless, I will do my best to describe its intention and the meaning underlying its parameters and syntax. Perhaps the most challenging mental hurdle that you must overcome here is that forEachSingleChild is the callback passed to the traverseAllChildren function that will be investigated in a moment. The bookKeeping parameter refers to the object returned by the ForEachBookKeeping class constructor, which as a reminder contains three properties: func, context, and count. The child parameter refers to the currently-exposed child component. Lastly, the name parameter is exposed for consistency but is not actually used in the function. The line destructures the func and context variables to equal bookKeeping.func and bookKeeping.context, respectively. Lastly, func is invoked on the child parameter, the bookKeeping.count value is incremented, and func’s this value is set to context parameter.

ReactChildren.js: lines 67–75

Lines 67–75: The forEachChildren function constitutes part of React Core’s top-level public API and is exposed as React.Children.forEach. The function accepts three parameters: children, which is a collection of child components, forEachFunc, which is the function to be invoked for each leaf child element, and forEachContext, an optional argument that becomes the forEachFunc’s (callback) value of this.

Let’s dissect this function further.

ReactChildren.js: lines 68–70

Lines 68–70: According to React’s Top-Level API documentation, forEach returns a nully value if children is a nully value. In the lines here, React checks for a nully value, and if it finds one, returns that nully value.

ReactChildren.js: lines 71–72

Lines 71–72: These lines invoke ForEachBookKeeping.getPooled, passing in forEachFunc and forEachContext as parameters. Remember, ForEachBookKeeping.getPooled is merely an alias for the twoArgumentPooler whose this value is bound to ForEachBookKeeping! Because ForEachBookKeeping.instancePool.length equals 0, a new instance of ForEachBookKeeping is assigned to the traverseContext variable on line 71.

ReactChildren.js: line 73

Line 73: This line marks our first introduction to the traverseAllChildren module.

Detour Part 2: The traverseAllChildren Module

The traverseAllChildren module recursively invokes a callback on each leaf child element in the component tree. It’s also important to note that it returns a number equal to the total number of leaf nodes in the component tree. The module can be found at react/src/shared/utils/traverseAllChildren.js. Instead of starting at the top and working our way down, as we have been, let’s begin at the function at the bottom traverseAllChildren.

traverseAllChildren.js: lines 213–219

Lines 213–219: These lines define the function that constitutes the traverseAllChildren API. The function accepts three parameters, children, the collection of child component elements and their sub-children components, callback, which in this case is actually the forEachSingleChild function, and the optional third parameter traverseContext. Once again, traverseAllChildren checks if children is equal to null or undefined and returns the number 0 if it is. On line 218, the function traverseAllChildrenImpl is invoked passing in children, the empty string ‘’, the forEachSingleChild callback, and the traverseContext.

traverseAllChildren.js: lines 45–54

Lines 45–54: The function defined in these lines is intended to identify a component within a set. getComponentKey accepts a component and its index as its only parameters. It does some typechecking on line 48, namely iff the component is defined, the component’s type is equal to ‘object’, and the key property on the component is not a nully value, the key is escaped so it is safe to use as a reactid. Otherwise, the component’s index in its collection is used as its reactid. The latter option is unfavorable, but works well enough.

traverseAllChildren.js: lines 64–95

Lines 64–195: This is the function that actually recursively traverses the children component tree. The function accepts four parameters: children, the child component tree, nameSoFar, which starts out as the empty string ‘’, callback, in this case the forEachSingleChild function and is the function that is invoked for each leaf node, and the traverseContext, callback’s this binding.

traverseAllChildren.js: line 70

Line 70: This line declares the variable type and initializes it to the children parameter’s type.

traverseAllChildren.js: lines 72–75

Line 72–75: These lines check the type variable for ‘undefined’ or ‘boolean’, and if either is strictly true the children variable is assigned to null.

traverseAllChildren.js: lines 77–91

Lines 77–91: These lines check if the value of children is null, the type variable holds either ‘string’ or ‘number’, or type is an object whose $$typeof property is the REACT_ELEMENT_TYPE variable. In other words they check if the current location has reached the so-called base case (i.e. reached a child leaf element). If any of these conditions are true, the callback parameter is invoked on traverseContext, children, and nameSoFar, respectively. It’s worth noting here that callback’s arity perfectly matches that of forEachSingleChild’s in the ReactChildren module. Finally, because this point must be a leaf in the component tree the number 1 is returned which will later increment the subTreeCount value.

traverseAllChildren.js: lines 98–108

Lines 98–108: If children is not one of the atomic types listed above nor the value null, it must be a collection of children. Line 98 checks if the children collection is an array. If children is an array, line 99 iterates through each of its elements. Line 100 names sets the utility variable child to be the current element of children at index i. The return value of getComponentKey is appended to nextNamePrefix and assigned to the variable nextName. Lastly, traverseAllChildrenImpl is recursively invoked on the child element. The return value of this call will be a number whose value will be added to the current value of the subTreeCount variable.

traverseAllChildren.js: line 110

Line 110: I won’t go into the how, but I will briefly touch on the why of this line. If the children collection is not an array, React will check if the collection has implemented the iterator protocol. The iterator protocol is a relatively recent addition to the Javascript language. According to MDN,

[t]he iterator protocol defines a standard way to produce a sequence of values (either finite or infinite).

An object is an iterator when it implements a next() method…

The return value of invoking the next method is an object that has two properties value and done, where done is a boolean whose value is ‘true if the iterator is past the end of the iterated sequence’ and ‘false if the iterator was able to produce the next value in the sequence.’ The value can hold any JavaScript value returned by the iterator. In this case, if an iterator function exists, it is stored in the iteratorFn variable.

traverseAllChildren.js: lines 111–125

Lines 111–125: These lines begin by checking if the value held in the iteratorFn variable exists. If it does, the function is partially applied, binding its this value to the children parameter. Line 114 checks that iteratorFn is not an alias for children.entries. If it is not, the step variable is declared. This variable will hold the object returned by the iterator.next invocation. On lines 116 the while loop will continue looping until the object returned by iterator.next's done property is false. On the next line the return value of getComponentKey is appended to nextNamePrefix and assigned to the nextName variable. Lastly, the traverseAllChildren function is recursively invoked for each loop of the while loop in the same manner above.

traverseAllChildren.js: lines 126–162

Lines 126–162: If, on the other hand the iteratorFn is equal to children.entries, the iteratorFn is assumed to be a JavaScript Map. If in the __DEV__ environment, React will throw a warning, saying ‘Using Maps as children is not yet fully supported…’ The didWarnAboutMaps flag is then flipped to true so the error won’t be thrown again. The remaining while loop in this block of code is the same as lines 111–125 above.

traverseAllChildren.js: lines 163–192

Lines 163–192: These lines begin by checking if the type of children is an object. If it is, the variable addendum is declared and initialized to the empty string. Next, React checks if it’s executing in the __DEV__ environment, and if it is, runs a series of checks that will overwrite the addendum variable. Then, line 182 invokes the String constructor on the children parameter. Lastly an invariant is thrown stating, ‘Objects are not valid as a React child…’

traverseAllChildren.js: line 194

Line 194: This line returns the value held by the subTreeCount variable which, once again, is the number of leaf child components in the component tree. It will accumulate the total number of leaf nodes as it is returned through the call stack.

Back to the ReactChildren Module

Returning to the ReactChildren module, we arrive at the MapBookKeeping function.

ReactChildren.js: lines 87–93

Lines 87–93: The MapBookKeeping is very similar to the ForEachBookKeeping. Both are class constructors and are passed arguments that represent a callback and context. The difference here, however, is that MapBookKeeping also requires the mapResult and keyPrefix arguments. The mapResult will ultimately be initialized to an empty array that will hold the mapped child components while the keyPrefix argument will be initialized to the empty string ‘’ and will ultimately prefix the React keys.

ReactChildren.js: lines 94–100

Lines 94–100: The destructor method on the MapBookKeeping.prototype is invoked following the traverseAllChildren call and is very similar in functionality to ForEachBookKeeping.prototype.destructor.

ReactChildren.js: line 101

Line 101: This line enacts the same functionality as the call to PooledClass.addPoolingTo above. The only difference, however, is that it extends the MapBookKeeping class constructor with the fourArgumentPooler rather than its twoArgumentPooler counterpart. The reason this function is extended with fourArgumentPooler rather than its counterpart is for the simple reason that MapBookKeeping accepts four arguments rather than two.

Let’s now jump down to the mapChildren function, and walk through a call to React.Children.map!

ReactChildren.js: lines 161–168

Lines 161–168: The function described here constitutes part of React Core’s top-level API and is exposed as React.Children.map. This function accepts three arguments: children, the collection of child components usually someInstance.props.children, func, the projection function invoked on each leaf child, and context which binds func’s this value. The first thing it does is checks if the children parameter is a nully value. If it is, mapChildren will early-return the children value. On line 165, a result variable is declared and initialized to an empty array. Because arrays are passed by reference, it can be passed along as React traverses the component tree and accumulate the mapped values. This is why the array is returned on line 167. Let’s now look at mapIntoWithKeyPrefixInternal.

ReactChildren.js: lines 133–146

Lines 133–146: These lines define the mapIntoWithKeyPrefixInternal functionality. The function’s arity is five, consisting of: children, the collection of child components passed into mapChildren, array, which is the result accumulator array, prefix, which is initially null but will hold a string value on subsequent recursive calls, func, the projection callback passed into mapChildren, and context, func’s this binding also passed into mapChildren. It then checks if the prefix argument is null and if so, escapes it by calling escapeUserProvidedKey. Otherwise, escapedPrefix will be the empty string. Next, MapBookKeeping.getPooled is invoked in the same way ForEachBookKeeping.getPooled is invoked in the code above; fourArgumentPooler is invoked with its this value bound to MapBookKeeping. This call then returns a new instance of MapBookKeeping. Next, traverseAllChildren is called and works in the same manner as when it is called in forEachChildren, only passing in the mapSingleChildIntoContext, which we’ll investigate now.

ReactChildren.js: lines 103–131

Lines 103–131: This brings us to the last function of this part of the series. It is the function that is called at each leaf child of the collection of children — we can show this by seeing that its arity matches that of traverseAllChildren’s callback parameter and that it occupies the second position of the traverseAllChildren call on line 144. One interesting thing to note about this function is that it is used for the side-effect it creates — notice its lack of explicit return. The first thing this function does is to destructure the result, keyPrefix, func, and context properties from our bookKeeping instance (an instance of MapBookKeeping). We then invoke our projection (mapping) function, func, binding the function’s this value to context, and passing in the child leaf element and the incremented bookKeeping.count value. Next, the mapped child is checked to see if it’s an array. We’ll investigate what happens when React finds an array as it traverses the tree in a moment when we look at the toArray function. If the value held by mappedChild is not an array, however, and is not a nully value and is a valid element, the component is cloned, given a new key, and pushed to the result array that gets returned by mapChildren.

ReactChildren.js: lines 185–187

Lines 185–187: The function defined here is extremely straight-forward. It simply counts the number of child leaf elements in a component tree. Notice that its callback on 172 always returns null, irrespective of its inputs. Also, notice that it’s the only function in the ReactChildren module that does some work with traverseAllChildren’s return value, which returns the number of leaf child elements. This demonstrates traverseAllChildren’s suprising composability!

ReactChildren.js: lines 196–205

Lines 196–205: This is the last function that we’ll investigate in this part of the series. It accepts a collection of children and returns an array with appropriately re-keyed children. The first thing this function does is creates a new array that will hold the flattened tree. Notice how mapIntoWithKeyPrefixInternal’s arity matches that of its invocation found on lines 108–113. If that function call’s return type is an array, that means the mapped array is a sub-tree, which must be traversed itself, so mapIntoWithKeyPrefixInternal is invoked on the child component. Interestingly, the new function passed as the projection function on line 112, is essentially an identity function, meaning the new children components are ‘mapped’ to themselves. Essentially, if it discovers an array as it’s traversing through the component tree, recursively traverse that sub-tree pushing its components along the way.

ReactChildren.js: lines 208–214

Lines 208–214: These lines expose the API for the ReactChildren module. For example, the forEachChildren function is exposed as React.Children.forEach.

That’s it. We made it…

--

--