translating React

react logo

by Jennifer Wong | @mybluewristband

jennz0r.github.io/translating-react

Who R U?

jenn riding a horse jenn riding a yak jenn holding an eagle jenn riding a camel

Mode

We're hiring!

https://mode.com/careers

A Little Story

About switching frameworks

OMG REACT!

COMPONENTS!

What even is React?

react logo

ReactJS

A JavaScript library for building user interfaces

Declarative

React will efficiently update and render just the right components when your data changes.

Declarative views make your code more predictable and easier to debug.

Component-Based

Build encapsulated components that manage their own state, then compose them to make complex UIs.

Learn Once, Write Anywhere

We don’t make assumptions about the rest of your technology stack, so you can develop new features in React without rewriting existing code.

React can also render on the server using Node and power mobile apps using React Native.

OKAY..

BUT..

I'm rewriting existing code RIGHT NOW.

What's confusing?

react logo

Honestly, sometimes I forget that React is actually just JavaScript.

COMPONENTS!

JSX!

VIRTUAL DOM!

Buuut, we're talking about React.

Oh yeah, it's open source!

GitHub

react/src/React.js


							import ReactVersion from 'shared/ReactVersion';
							import {
								REACT_ASYNC_MODE_TYPE,
								REACT_FRAGMENT_TYPE,
								REACT_PROFILER_TYPE,
								REACT_STRICT_MODE_TYPE,
								REACT_PLACEHOLDER_TYPE,
							} from 'shared/ReactSymbols';
							import {enableSuspense} from 'shared/ReactFeatureFlags';

							import {Component, PureComponent} from './ReactBaseClasses';
							import {createRef} from './ReactCreateRef';
							import {forEach, map, count, toArray, only} from './ReactChildren';
							import {
								createElement,
								createFactory,
								cloneElement,
								isValidElement,
							} from './ReactElement';
							import {createContext} from './ReactContext';
							import forwardRef from './forwardRef';
							import {
								createElementWithValidation,
								createFactoryWithValidation,
								cloneElementWithValidation,
							} from './ReactElementValidator';
							import ReactSharedInternals from './ReactSharedInternals';

							const React = {
								Children: {
									map,
									forEach,
									count,
									toArray,
									only,
								},

								createRef,
								Component,
								PureComponent,

								createContext,
								forwardRef,

								Fragment: REACT_FRAGMENT_TYPE,
								StrictMode: REACT_STRICT_MODE_TYPE,
								unstable_AsyncMode: REACT_ASYNC_MODE_TYPE,
								unstable_Profiler: REACT_PROFILER_TYPE,

								createElement: __DEV__ ? createElementWithValidation : createElement,
								cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
								createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
								isValidElement: isValidElement,

								version: ReactVersion,

								__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals,
							};

							if (enableSuspense) {
								React.Placeholder = REACT_PLACEHOLDER_TYPE;
							}

							export default React;
						

This is literally all React is.

Time to get your hardhats on!

Let's do some code spelunking!

react/packages/react/src/React.js


							import ReactVersion from 'shared/ReactVersion';
							import { ... } from 'shared/ReactSymbols';
							import { ... } from 'shared/ReactFeatureFlags';
							import { ... } from './ReactBaseClasses';
							import { ... } from './ReactCreateRef';
							import { ... } from './ReactChildren';
							import { ... } from './ReactElement';
							import { ... } from './ReactContext';
							import { ... } from './forwardRef';
							import { ... } from './ReactElementValidator';
							import ReactSharedInternals from './ReactSharedInternals';
						

react/packages/react/src/React.js


							const React = {
								Children: {
									map,
									forEach,
									count,
									toArray,
									only,
								},

								createRef,
								Component,
								PureComponent,

								createContext,
								forwardRef,

								Fragment: REACT_FRAGMENT_TYPE,
								StrictMode: REACT_STRICT_MODE_TYPE,
								unstable_AsyncMode: REACT_ASYNC_MODE_TYPE,
								unstable_Profiler: REACT_PROFILER_TYPE,

								createElement: __DEV__ ? createElementWithValidation : createElement,
								cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
								createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
								isValidElement: isValidElement,

								version: ReactVersion,

								__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals,
							};
						

IT'S JUST AN OBJECT!

Now that we know what React is, kinda?

Let's look at the basis of React!

React
Elements

react logo

JavaScript


							const rootElement = document.getElementById('root');

							const element = document.createElement('div');
							element.textContent = "Always together, never div-ided";
							element.className = "protest-div";

							rootElement.appendChild(element);
						

React


							const rootElement = document.getElementById('root');

							const element = React.createElement(
								'div',
								{ className: 'protest-div' },
								'Always together, never div-ided'
							);

							ReactDOM.render(element, rootElement);
						

							const rootElement = document.getElementById('root');
							const element = document.createElement('div');
							element.textContent = 'Always together, never div-ided';
							element.className = 'protest-div';
							rootElement.appendChild(element);
						


							const rootElement = document.getElementById('root');
							
							const element = React.createElement(
								'div',
								{ className: 'protest-div' },
								'Always together, never div-ided'
							);

							ReactDOM.render(element, rootElement);
						

Well, if that's the case, why have React.createElement?

React.createElement

Where's it come from?

react/src/React.js


							import {
								createElement,
								createFactory,
								cloneElement,
								isValidElement,
							} from './ReactElement';
						

react/packages/react/src/React.js

to

react/packages/react/src/ReactElement.js

createElement in ReactElement.js


							export function createElement(type, config, children) {
								let propName;

								// Reserved names are extracted
								const props = {};

								let key = null;
								let ref = null;
								let self = null;
								let source = null;

								if (config != null) {
									if (hasValidRef(config)) {
										ref = config.ref;
									}
									if (hasValidKey(config)) {
										key = '' + config.key;
									}

									self = config.__self === undefined ? null : config.__self;
									source = config.__source === undefined ? null : config.__source;
									// Remaining properties are added to a new props object
									for (propName in config) {
										if (
											hasOwnProperty.call(config, propName) &&
											!RESERVED_PROPS.hasOwnProperty(propName)
										) {
											props[propName] = config[propName];
										}
									}
								}

								// Children can be more than one argument, and those are transferred onto
								// the newly allocated props object.
								const childrenLength = arguments.length - 2;
								if (childrenLength === 1) {
									props.children = children;
								} else if (childrenLength > 1) {
									const childArray = Array(childrenLength);
									for (let i = 0; i < childrenLength; i++) {
										childArray[i] = arguments[i + 2];
									}
									if (__DEV__) {
										if (Object.freeze) {
											Object.freeze(childArray);
										}
									}
									props.children = childArray;
								}

								// Resolve default props
								if (type && type.defaultProps) {
									const defaultProps = type.defaultProps;
									for (propName in defaultProps) {
										if (props[propName] === undefined) {
											props[propName] = defaultProps[propName];
										}
									}
								}
								if (__DEV__) {
									if (key || ref) {
										if (
											typeof props.$$typeof === 'undefined' ||
											props.$$typeof !== REACT_ELEMENT_TYPE
										) {
											const displayName =
												typeof type === 'function'
													? type.displayName || type.name || 'Unknown'
													: type;
											if (key) {
												defineKeyPropWarningGetter(props, displayName);
											}
											if (ref) {
												defineRefPropWarningGetter(props, displayName);
											}
										}
									}
								}
								return ReactElement(
									type,
									key,
									ref,
									self,
									source,
									ReactCurrentOwner.current,
									props,
								);
							}
						

createElement


							export function createElement(type, config, children) {
						

							return ReactElement (
								type,
								key,
								ref,
								self,
								source,
								ReactCurrentOwner.current,
								props,
							);
						

So.. what's a ReactElement?

ReactElement in ReactElement.js


							/**
							 * Factory method to create a new React element. This no longer adheres to
							 * the class pattern, so do not use new to call it. Also, no instanceof check
							 * will work. Instead test $$typeof field against Symbol.for('react.element') to check
							 * if something is a React Element.
							 *
							 * @param {*} type
							 * @param {*} key
							 * @param {string|object} ref
							 * @param {*} self A *temporary* helper to detect places where `this` is
							 * different from the `owner` when React.createElement is called, so that we
							 * can warn. We want to get rid of owner and replace string `ref`s with arrow
							 * functions, and as long as `this` and owner are the same, there will be no
							 * change in behavior.
							 * @param {*} source An annotation object (added by a transpiler or otherwise)
							 * indicating filename, line number, and/or other information.
							 * @param {*} owner
							 * @param {*} props
							 * @internal
							 */
						

ReactElement in ReactElement.js


							const ReactElement = function(type, key, ref, self, source, owner, props) {
								const element = {
									// This tag allows us to uniquely identify this as a React Element
									$$typeof: REACT_ELEMENT_TYPE,

									// Built-in properties that belong on the element
									type: type,
									key: key,
									ref: ref,
									props: props,

									// Record the component responsible for creating this element.
									_owner: owner,
								};

								if (__DEV__) {
									// The validation flag is currently mutative. We put it on
									// an external backing store so that we can freeze the whole object.
									// This can be replaced with a WeakMap once they are implemented in
									// commonly used development environments.
									element._store = {};

									// To make comparing ReactElements easier for testing purposes, we make
									// the validation flag non-enumerable (where possible, which should
									// include every environment we run tests in), so the test framework
									// ignores it.
									Object.defineProperty(element._store, 'validated', {
										configurable: false,
										enumerable: false,
										writable: true,
										value: false,
									});
									// self and source are DEV only properties.
									Object.defineProperty(element, '_self', {
										configurable: false,
										enumerable: false,
										writable: false,
										value: self,
									});
									// Two elements created in two different places should be considered
									// equal for testing purposes and therefore we hide it from enumeration.
									Object.defineProperty(element, '_source', {
										configurable: false,
										enumerable: false,
										writable: false,
										value: source,
									});
									if (Object.freeze) {
										Object.freeze(element.props);
										Object.freeze(element);
									}
								}

								return element;
							};
						

							const element = {
								// This tag allows us to uniquely identify this as a React Element
								$$typeof: REACT_ELEMENT_TYPE,

								// Built-in properties that belong on the element
								type: type,
								key: key,
								ref: ref,
								props: props,

								// Record the component responsible for creating this element.
								_owner: owner,
							};
							.
							.
							return element;
						

OH IT'S JUST ANOTHER OBJECT!

But what are all those properties?

REACT MAGIC!

Built-in properties that belong on the element

  • type
  • key
  • ref
  • props

But.. we're just scratching the surface of React here.

And now we've come full circle!


							export function createElement(type, config, children) {
								let propName;

								// Reserved names are extracted
								const props = {};

								let key = null;
								let ref = null;
								.
								.
								return ReactElement(
									type,
									key,
									ref,
									.
									.
									props,
								);
							}
						
							
							const element = React.createElement(
								'div',
								{ className: 'protest-div' },
								'Always together, never div-ided'
							);
						

So now we've gone from

React.createElement in React.js

to

React.createElement in ReactElement.js

to

ReactElement in ReactElement.js

BUT GUESS WHAT

There's an easier way.

JSX

JavaScript XML

XML-like syntax extension to ECMAScript

JavaScript XML

JSX is just syntactic sugar for calling React.createElement(component, props, ...children)

Syntatic SUGAR

JavaScript XML

So, anything you can do with JSX can also be done with just plain JavaScript!

JSX v. JavaScript

video from annie get your gun, song anything you can do i can do better

React + JSX

React comes with the default set of syntax-transformers that ship with Babel

Don't forget the React!

Since JSX compiles into calls to React.createElement, the React library must always be in scope of your JSX code.

don't over the breakfast club gif you over the breakfast club gif

Don't you forget about React.

forget over the breakfast club gif 'about me' over the breakfast club gif

Transpiled JSX

Babel Transpilation of our original ReactElement

Protected Words

Since JSX is closer to JavaScript than to HTML, React DOM uses camelCase property naming convention instead of HTML attribute names.


							// When adding attributes to the HTML or SVG whitelist, be sure to
							// also add them to this module to ensure casing and incorrect name
							// warnings.
							const possibleStandardNames = {
								// HTML
								accept: 'accept',
								acceptcharset: 'acceptCharset',
								'accept-charset': 'acceptCharset',
								accesskey: 'accessKey',
								action: 'action',
								allowfullscreen: 'allowFullScreen',
								alt: 'alt',
								as: 'as',
								async: 'async',
								autocapitalize: 'autoCapitalize',
								autocomplete: 'autoComplete',
								autocorrect: 'autoCorrect',
								autofocus: 'autoFocus',
								autoplay: 'autoPlay',
								autosave: 'autoSave',
								capture: 'capture',
								cellpadding: 'cellPadding',
								cellspacing: 'cellSpacing',
								challenge: 'challenge',
								charset: 'charSet',
								checked: 'checked',
								children: 'children',
								cite: 'cite',
								class: 'className',
								classid: 'classID',
								classname: 'className',
								cols: 'cols',
								colspan: 'colSpan',
								content: 'content',
								contenteditable: 'contentEditable',
								contextmenu: 'contextMenu',
								controls: 'controls',
								controlslist: 'controlsList',
								coords: 'coords',
								crossorigin: 'crossOrigin',
								dangerouslysetinnerhtml: 'dangerouslySetInnerHTML',
								data: 'data',
								datetime: 'dateTime',
								default: 'default',
								defaultchecked: 'defaultChecked',
								defaultvalue: 'defaultValue',
								defer: 'defer',
								dir: 'dir',
								disabled: 'disabled',
								download: 'download',
								draggable: 'draggable',
								enctype: 'encType',
								for: 'htmlFor',
								form: 'form',
								formmethod: 'formMethod',
								formaction: 'formAction',
								formenctype: 'formEncType',
								formnovalidate: 'formNoValidate',
								formtarget: 'formTarget',
								frameborder: 'frameBorder',
								headers: 'headers',
								height: 'height',
								hidden: 'hidden',
								high: 'high',
								href: 'href',
								hreflang: 'hrefLang',
								htmlfor: 'htmlFor',
								httpequiv: 'httpEquiv',
								'http-equiv': 'httpEquiv',
								icon: 'icon',
								id: 'id',
								innerhtml: 'innerHTML',
								inputmode: 'inputMode',
								integrity: 'integrity',
								is: 'is',
								itemid: 'itemID',
								itemprop: 'itemProp',
								itemref: 'itemRef',
								itemscope: 'itemScope',
								itemtype: 'itemType',
								keyparams: 'keyParams',
								keytype: 'keyType',
								kind: 'kind',
								label: 'label',
								lang: 'lang',
								list: 'list',
								loop: 'loop',
								low: 'low',
								manifest: 'manifest',
								marginwidth: 'marginWidth',
								marginheight: 'marginHeight',
								max: 'max',
								maxlength: 'maxLength',
								media: 'media',
								mediagroup: 'mediaGroup',
								method: 'method',
								min: 'min',
								minlength: 'minLength',
								multiple: 'multiple',
								muted: 'muted',
								name: 'name',
								nomodule: 'noModule',
								nonce: 'nonce',
								novalidate: 'noValidate',
								open: 'open',
								optimum: 'optimum',
								pattern: 'pattern',
								placeholder: 'placeholder',
								playsinline: 'playsInline',
								poster: 'poster',
								preload: 'preload',
								profile: 'profile',
								radiogroup: 'radioGroup',
								readonly: 'readOnly',
								referrerpolicy: 'referrerPolicy',
								rel: 'rel',
								required: 'required',
								reversed: 'reversed',
								role: 'role',
								rows: 'rows',
								rowspan: 'rowSpan',
								sandbox: 'sandbox',
								scope: 'scope',
								scoped: 'scoped',
								scrolling: 'scrolling',
								seamless: 'seamless',
								selected: 'selected',
								shape: 'shape',
								size: 'size',
								sizes: 'sizes',
								span: 'span',
								spellcheck: 'spellCheck',
								src: 'src',
								srcdoc: 'srcDoc',
								srclang: 'srcLang',
								srcset: 'srcSet',
								start: 'start',
								step: 'step',
								style: 'style',
								summary: 'summary',
								tabindex: 'tabIndex',
								target: 'target',
								title: 'title',
								type: 'type',
								usemap: 'useMap',
								value: 'value',
								width: 'width',
								wmode: 'wmode',
								wrap: 'wrap',

								// SVG
								about: 'about',
								accentheight: 'accentHeight',
								'accent-height': 'accentHeight',
								accumulate: 'accumulate',
								additive: 'additive',
								alignmentbaseline: 'alignmentBaseline',
								'alignment-baseline': 'alignmentBaseline',
								allowreorder: 'allowReorder',
								alphabetic: 'alphabetic',
								amplitude: 'amplitude',
								arabicform: 'arabicForm',
								'arabic-form': 'arabicForm',
								ascent: 'ascent',
								attributename: 'attributeName',
								attributetype: 'attributeType',
								autoreverse: 'autoReverse',
								azimuth: 'azimuth',
								basefrequency: 'baseFrequency',
								baselineshift: 'baselineShift',
								'baseline-shift': 'baselineShift',
								baseprofile: 'baseProfile',
								bbox: 'bbox',
								begin: 'begin',
								bias: 'bias',
								by: 'by',
								calcmode: 'calcMode',
								capheight: 'capHeight',
								'cap-height': 'capHeight',
								clip: 'clip',
								clippath: 'clipPath',
								'clip-path': 'clipPath',
								clippathunits: 'clipPathUnits',
								cliprule: 'clipRule',
								'clip-rule': 'clipRule',
								color: 'color',
								colorinterpolation: 'colorInterpolation',
								'color-interpolation': 'colorInterpolation',
								colorinterpolationfilters: 'colorInterpolationFilters',
								'color-interpolation-filters': 'colorInterpolationFilters',
								colorprofile: 'colorProfile',
								'color-profile': 'colorProfile',
								colorrendering: 'colorRendering',
								'color-rendering': 'colorRendering',
								contentscripttype: 'contentScriptType',
								contentstyletype: 'contentStyleType',
								cursor: 'cursor',
								cx: 'cx',
								cy: 'cy',
								d: 'd',
								datatype: 'datatype',
								decelerate: 'decelerate',
								descent: 'descent',
								diffuseconstant: 'diffuseConstant',
								direction: 'direction',
								display: 'display',
								divisor: 'divisor',
								dominantbaseline: 'dominantBaseline',
								'dominant-baseline': 'dominantBaseline',
								dur: 'dur',
								dx: 'dx',
								dy: 'dy',
								edgemode: 'edgeMode',
								elevation: 'elevation',
								enablebackground: 'enableBackground',
								'enable-background': 'enableBackground',
								end: 'end',
								exponent: 'exponent',
								externalresourcesrequired: 'externalResourcesRequired',
								fill: 'fill',
								fillopacity: 'fillOpacity',
								'fill-opacity': 'fillOpacity',
								fillrule: 'fillRule',
								'fill-rule': 'fillRule',
								filter: 'filter',
								filterres: 'filterRes',
								filterunits: 'filterUnits',
								floodopacity: 'floodOpacity',
								'flood-opacity': 'floodOpacity',
								floodcolor: 'floodColor',
								'flood-color': 'floodColor',
								focusable: 'focusable',
								fontfamily: 'fontFamily',
								'font-family': 'fontFamily',
								fontsize: 'fontSize',
								'font-size': 'fontSize',
								fontsizeadjust: 'fontSizeAdjust',
								'font-size-adjust': 'fontSizeAdjust',
								fontstretch: 'fontStretch',
								'font-stretch': 'fontStretch',
								fontstyle: 'fontStyle',
								'font-style': 'fontStyle',
								fontvariant: 'fontVariant',
								'font-variant': 'fontVariant',
								fontweight: 'fontWeight',
								'font-weight': 'fontWeight',
								format: 'format',
								from: 'from',
								fx: 'fx',
								fy: 'fy',
								g1: 'g1',
								g2: 'g2',
								glyphname: 'glyphName',
								'glyph-name': 'glyphName',
								glyphorientationhorizontal: 'glyphOrientationHorizontal',
								'glyph-orientation-horizontal': 'glyphOrientationHorizontal',
								glyphorientationvertical: 'glyphOrientationVertical',
								'glyph-orientation-vertical': 'glyphOrientationVertical',
								glyphref: 'glyphRef',
								gradienttransform: 'gradientTransform',
								gradientunits: 'gradientUnits',
								hanging: 'hanging',
								horizadvx: 'horizAdvX',
								'horiz-adv-x': 'horizAdvX',
								horizoriginx: 'horizOriginX',
								'horiz-origin-x': 'horizOriginX',
								ideographic: 'ideographic',
								imagerendering: 'imageRendering',
								'image-rendering': 'imageRendering',
								in2: 'in2',
								in: 'in',
								inlist: 'inlist',
								intercept: 'intercept',
								k1: 'k1',
								k2: 'k2',
								k3: 'k3',
								k4: 'k4',
								k: 'k',
								kernelmatrix: 'kernelMatrix',
								kernelunitlength: 'kernelUnitLength',
								kerning: 'kerning',
								keypoints: 'keyPoints',
								keysplines: 'keySplines',
								keytimes: 'keyTimes',
								lengthadjust: 'lengthAdjust',
								letterspacing: 'letterSpacing',
								'letter-spacing': 'letterSpacing',
								lightingcolor: 'lightingColor',
								'lighting-color': 'lightingColor',
								limitingconeangle: 'limitingConeAngle',
								local: 'local',
								markerend: 'markerEnd',
								'marker-end': 'markerEnd',
								markerheight: 'markerHeight',
								markermid: 'markerMid',
								'marker-mid': 'markerMid',
								markerstart: 'markerStart',
								'marker-start': 'markerStart',
								markerunits: 'markerUnits',
								markerwidth: 'markerWidth',
								mask: 'mask',
								maskcontentunits: 'maskContentUnits',
								maskunits: 'maskUnits',
								mathematical: 'mathematical',
								mode: 'mode',
								numoctaves: 'numOctaves',
								offset: 'offset',
								opacity: 'opacity',
								operator: 'operator',
								order: 'order',
								orient: 'orient',
								orientation: 'orientation',
								origin: 'origin',
								overflow: 'overflow',
								overlineposition: 'overlinePosition',
								'overline-position': 'overlinePosition',
								overlinethickness: 'overlineThickness',
								'overline-thickness': 'overlineThickness',
								paintorder: 'paintOrder',
								'paint-order': 'paintOrder',
								panose1: 'panose1',
								'panose-1': 'panose1',
								pathlength: 'pathLength',
								patterncontentunits: 'patternContentUnits',
								patterntransform: 'patternTransform',
								patternunits: 'patternUnits',
								pointerevents: 'pointerEvents',
								'pointer-events': 'pointerEvents',
								points: 'points',
								pointsatx: 'pointsAtX',
								pointsaty: 'pointsAtY',
								pointsatz: 'pointsAtZ',
								prefix: 'prefix',
								preservealpha: 'preserveAlpha',
								preserveaspectratio: 'preserveAspectRatio',
								primitiveunits: 'primitiveUnits',
								property: 'property',
								r: 'r',
								radius: 'radius',
								refx: 'refX',
								refy: 'refY',
								renderingintent: 'renderingIntent',
								'rendering-intent': 'renderingIntent',
								repeatcount: 'repeatCount',
								repeatdur: 'repeatDur',
								requiredextensions: 'requiredExtensions',
								requiredfeatures: 'requiredFeatures',
								resource: 'resource',
								restart: 'restart',
								result: 'result',
								results: 'results',
								rotate: 'rotate',
								rx: 'rx',
								ry: 'ry',
								scale: 'scale',
								security: 'security',
								seed: 'seed',
								shaperendering: 'shapeRendering',
								'shape-rendering': 'shapeRendering',
								slope: 'slope',
								spacing: 'spacing',
								specularconstant: 'specularConstant',
								specularexponent: 'specularExponent',
								speed: 'speed',
								spreadmethod: 'spreadMethod',
								startoffset: 'startOffset',
								stddeviation: 'stdDeviation',
								stemh: 'stemh',
								stemv: 'stemv',
								stitchtiles: 'stitchTiles',
								stopcolor: 'stopColor',
								'stop-color': 'stopColor',
								stopopacity: 'stopOpacity',
								'stop-opacity': 'stopOpacity',
								strikethroughposition: 'strikethroughPosition',
								'strikethrough-position': 'strikethroughPosition',
								strikethroughthickness: 'strikethroughThickness',
								'strikethrough-thickness': 'strikethroughThickness',
								string: 'string',
								stroke: 'stroke',
								strokedasharray: 'strokeDasharray',
								'stroke-dasharray': 'strokeDasharray',
								strokedashoffset: 'strokeDashoffset',
								'stroke-dashoffset': 'strokeDashoffset',
								strokelinecap: 'strokeLinecap',
								'stroke-linecap': 'strokeLinecap',
								strokelinejoin: 'strokeLinejoin',
								'stroke-linejoin': 'strokeLinejoin',
								strokemiterlimit: 'strokeMiterlimit',
								'stroke-miterlimit': 'strokeMiterlimit',
								strokewidth: 'strokeWidth',
								'stroke-width': 'strokeWidth',
								strokeopacity: 'strokeOpacity',
								'stroke-opacity': 'strokeOpacity',
								suppresscontenteditablewarning: 'suppressContentEditableWarning',
								suppresshydrationwarning: 'suppressHydrationWarning',
								surfacescale: 'surfaceScale',
								systemlanguage: 'systemLanguage',
								tablevalues: 'tableValues',
								targetx: 'targetX',
								targety: 'targetY',
								textanchor: 'textAnchor',
								'text-anchor': 'textAnchor',
								textdecoration: 'textDecoration',
								'text-decoration': 'textDecoration',
								textlength: 'textLength',
								textrendering: 'textRendering',
								'text-rendering': 'textRendering',
								to: 'to',
								transform: 'transform',
								typeof: 'typeof',
								u1: 'u1',
								u2: 'u2',
								underlineposition: 'underlinePosition',
								'underline-position': 'underlinePosition',
								underlinethickness: 'underlineThickness',
								'underline-thickness': 'underlineThickness',
								unicode: 'unicode',
								unicodebidi: 'unicodeBidi',
								'unicode-bidi': 'unicodeBidi',
								unicoderange: 'unicodeRange',
								'unicode-range': 'unicodeRange',
								unitsperem: 'unitsPerEm',
								'units-per-em': 'unitsPerEm',
								unselectable: 'unselectable',
								valphabetic: 'vAlphabetic',
								'v-alphabetic': 'vAlphabetic',
								values: 'values',
								vectoreffect: 'vectorEffect',
								'vector-effect': 'vectorEffect',
								version: 'version',
								vertadvy: 'vertAdvY',
								'vert-adv-y': 'vertAdvY',
								vertoriginx: 'vertOriginX',
								'vert-origin-x': 'vertOriginX',
								vertoriginy: 'vertOriginY',
								'vert-origin-y': 'vertOriginY',
								vhanging: 'vHanging',
								'v-hanging': 'vHanging',
								videographic: 'vIdeographic',
								'v-ideographic': 'vIdeographic',
								viewbox: 'viewBox',
								viewtarget: 'viewTarget',
								visibility: 'visibility',
								vmathematical: 'vMathematical',
								'v-mathematical': 'vMathematical',
								vocab: 'vocab',
								widths: 'widths',
								wordspacing: 'wordSpacing',
								'word-spacing': 'wordSpacing',
								writingmode: 'writingMode',
								'writing-mode': 'writingMode',
								x1: 'x1',
								x2: 'x2',
								x: 'x',
								xchannelselector: 'xChannelSelector',
								xheight: 'xHeight',
								'x-height': 'xHeight',
								xlinkactuate: 'xlinkActuate',
								'xlink:actuate': 'xlinkActuate',
								xlinkarcrole: 'xlinkArcrole',
								'xlink:arcrole': 'xlinkArcrole',
								xlinkhref: 'xlinkHref',
								'xlink:href': 'xlinkHref',
								xlinkrole: 'xlinkRole',
								'xlink:role': 'xlinkRole',
								xlinkshow: 'xlinkShow',
								'xlink:show': 'xlinkShow',
								xlinktitle: 'xlinkTitle',
								'xlink:title': 'xlinkTitle',
								xlinktype: 'xlinkType',
								'xlink:type': 'xlinkType',
								xmlbase: 'xmlBase',
								'xml:base': 'xmlBase',
								xmllang: 'xmlLang',
								'xml:lang': 'xmlLang',
								xmlns: 'xmlns',
								'xml:space': 'xmlSpace',
								xmlnsxlink: 'xmlnsXlink',
								'xmlns:xlink': 'xmlnsXlink',
								xmlspace: 'xmlSpace',
								y1: 'y1',
								y2: 'y2',
								y: 'y',
								ychannelselector: 'yChannelSelector',
								z: 'z',
								zoomandpan: 'zoomAndPan',
							};

							export default possibleStandardNames;
						

498 lines?!

CURLY BRACES

What are you???

facebook.github.io/jsx/



							JSXAttributeValue :

							{ AssignmentExpression }

							JSXChild :
							{ JSXChildExpression }
						

It's JavaScript! Living inside a JSX world!

We are living in a JSX world.
And I am a JavaScript girl.

Now that we have an easier way of using React.createElement, let's make some...

COMPONENTS!

React COMPONENT

react logo

React.Component


							function Component(props, context, updater) {
								this.props = props;
								this.context = context;

								// If a component has string refs,
								// we will assign a different object later.
								this.refs = emptyObject;

								// We initialize the default updater but
								// the real one gets injected by therenderer.
								this.updater = updater || ReactNoopUpdateQueue;
							}
						

Gimme my props!


							const rootElement = document.getElementById('root');

							const element = React.createElement(
								'div',
								{ className: 'protest-div' },
								'Always together, never div-ided'
							);

							ReactDOM.render(element, rootElement);
						
	
							class Protest extends React.Component {
								render() {
									return <div className="protest-div">
										{this.props.chant}
									</div>;
								}
							}

							const rootElement = document.getElementById('root');

							const element = <Protest chant="Always together, never div-ided." />;

							ReactDOM.render(element, rootElement);
						

React State

react logo

setState

react/packages/react/src/ReactBaseClasses.js

	
								/**
								* Sets a subset of the state. Always use this to mutate
								* state. You should treat `this.state` as immutable.
								*
								* There is no guarantee that `this.state` will be immediately updated, so
								* accessing `this.state` after calling this method may return the old value.
								*
								* There is no guarantee that calls to `setState` will run synchronously,
								* as they may eventually be batched together.  You can provide an optional
								* callback that will be executed when the call to setState is actually
								* completed.
								*
								* When a function is provided to setState, it will be called at some point in
								* the future (not synchronously). It will be called with the up to date
								* component arguments (state, props, context). These values can be different
								* from this.* because your function may be called after receiveProps but before
								* shouldComponentUpdate, and this new state, props, and context will not yet be
								* assigned to this.
								*
								* @param {object|function} partialState Next partial state or function to
								*        produce next partial state to be merged with current state.
								* @param {?function} callback Called after state is updated.
								* @final
								* @protected
								*/
							Component.prototype.setState = function(partialState, callback) {
								invariant(
									typeof partialState === 'object' ||
										typeof partialState === 'function' ||
										partialState == null,
									'setState(...): takes an object of state variables to update or a ' +
										'function which returns an object of state variables.',
								);
								this.updater.enqueueSetState(this, partialState, callback, 'setState');
							};							 
						

Notes on State

  • `setState` sets a subset of the state.
  • Treat `this.state` as immutable.
  • No guarantee `this.state` will be immediately updated.
  • No guarantee that calls to `setState` run synchronously.
  • Your function may be called after `receiveProps` but before `shouldComponentUpdate`

receiveProps

react/packages/react-reconciler/src/ReactFiberClassComponent.js


							function callComponentWillReceiveProps(
								workInProgress,
								instance,
								newProps,
								nextContext,
							) {
								const oldState = instance.state;
								startPhaseTimer(workInProgress, 'componentWillReceiveProps');
								if (typeof instance.componentWillReceiveProps === 'function') {
									instance.componentWillReceiveProps(newProps, nextContext);
								}
								if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
									instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);
								}
								stopPhaseTimer();

								if (instance.state !== oldState) {
									// Removed some stuff related to being in Dev environment
									classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
								}
							}
						

shouldComponentUpdate


						function checkShouldComponentUpdate(
							workInProgress,
							ctor,
							oldProps,
							newProps,
							oldState,
							newState,
							nextContext,
						) {
							const instance = workInProgress.stateNode;
							if (typeof instance.shouldComponentUpdate === 'function') {
								startPhaseTimer(workInProgress, 'shouldComponentUpdate');
								const shouldUpdate = instance.shouldComponentUpdate(
									newProps,
									newState,
									nextContext,
								);
								stopPhaseTimer();

								if (__DEV__) {
									warningWithoutStack(
										shouldUpdate !== undefined,
										'%s.shouldComponentUpdate(): Returned undefined instead of a ' +
											'boolean value. Make sure to return true or false.',
										getComponentName(ctor) || 'Component',
									);
								}

								return shouldUpdate;
							}

							if (ctor.prototype && ctor.prototype.isPureReactComponent) {
								return (
									!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
								);
							}

							return true;
						}
						

react/packages/react-reconciler/src/ReactFiberClassComponent.js


							instance.componentWillReceiveProps(
								newProps,
								nextContext
							);
						

							const shouldUpdate = instance.shouldComponentUpdate(
								newProps,
								newState,
								nextContext,
							);

							return shouldUpdate;
						

What's the instance?


							const instance = workInProgress.stateNode;
						

What's the workInProgress?


							// Invokes the update life-cycles and returns false if it shouldn't rerender.
							function updateClassInstance(
								current: Fiber,
								workInProgress: Fiber,
								ctor: any,
								newProps: any,
								renderExpirationTime: ExpirationTime,
							)
						

What's the stateNode?

stateNode

Holds the reference to the class instance of a component, a DOM node, or other React element type associated with the fiber node. In general, we can say that this property is used to hold the local state associated with a fiber.

- InDepth

More on Fiber later...

	
							class Protest extends React.Component {
								constructor(props) {
									super(props);
									this.state = {
										emergency: true,
										police: true
									};
								}

								componentDidMount() {
									this.setState({
										police: false
									})
								}

								render() {
									return <div className="protest-div">
										<p>{this.props.chant}</p>
										<p>Is there a state of emergency? {this.state.emergency ? 'yes' : 'no'}</p>
										<p>Are we in a police state? {this.state.police ? 'yes' : 'no'}</p>
									</div>;
								}
							}

							const rootElement = document.getElementById('root');

							const element = <Protest chant="Always together, never div-ided." />;

							ReactDOM.render(element, rootElement);
						

Now that we have a component, let's render it to the DOM

React
DOM

react logo

react/packages/react-dom/

  • react-dom
    • findDOMNode
    • render
    • unmountComponentAtNode

react/packages/react-dom/src/client/ReactDOM.js

render


							render(
								element: React$Element,
								container: DOMContainer,
								callback: ?Function,
							) {
								return legacyRenderSubtreeIntoContainer(
									null,
									element,
									container,
									false,
									callback,
								);
							},
						

react/packages/react-dom/src/client/ReactDOM.js

legacyRenderSubtreeIntoContainer


							function legacyRenderSubtreeIntoContainer(
								parentComponent: ?React$Component,
								children: ReactNodeList,
								container: DOMContainer,
								forceHydrate: boolean,
								callback: ?Function,
							) {
								// TODO: Ensure all entry points contain this check
								invariant(
									isValidContainer(container),
									'Target container is not a DOM element.',
								);

								if (__DEV__) {
									topLevelUpdateWarnings(container);
								}

								// TODO: Without `any` type, Flow says "Property cannot be accessed on any
								// member of intersection type." Whyyyyyy.
								let root: Root = (container._reactRootContainer: any);
								if (!root) {
									// Initial mount
									root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
										container,
										forceHydrate,
									);
									if (typeof callback === 'function') {
										const originalCallback = callback;
										callback = function() {
											const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
											originalCallback.call(instance);
										};
									}
									// Initial mount should not be batched.
									DOMRenderer.unbatchedUpdates(() => {
										if (parentComponent != null) {
											root.legacy_renderSubtreeIntoContainer(
												parentComponent,
												children,
												callback,
											);
										} else {
											root.render(children, callback);
										}
									});
								} else {
									if (typeof callback === 'function') {
										const originalCallback = callback;
										callback = function() {
											const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
											originalCallback.call(instance);
										};
									}
									// Update
									if (parentComponent != null) {
										root.legacy_renderSubtreeIntoContainer(
											parentComponent,
											children,
											callback,
										);
									} else {
										root.render(children, callback);
									}
								}
								return DOMRenderer.getPublicRootInstance(root._internalRoot);
							}
						

							return DOMRenderer.getPublicRootInstance(root._internalRoot);
						


							import * as DOMRenderer from 'react-reconciler/inline.dom';
						

ReactFiberReconciler

React

Fiber

React Fiber is an internal engine change.

- FreeCodeCamp

Reconciler

React will walk through the virtual tree, find what’s changed, and update as little as it can.

This could mean updating one class attribute, or it could mean tearing down the whole DOM. This is Reconciliation.

- FreeCodeCamp

MORE REACT MAGIC

react/packages/react-reconciler/src/ReactFiberClassComponent.js

Secrets?

__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED

React.js

to

ReactSharedInternals.js

to

ReactCurrentOwner.js

to

react-reconciler

react-reconciler

This is an experimental package for creating custom React renderers.

Its API is not as stable as that of React, React Native, or React DOM, and does not follow the common versioning scheme.

Use it at your own risk.

REACT MAGIC

angular logo
person holding a sign that says free shrugs
componentsconf legacy_renderSubtreeIntoContainer

COMPONENTS!

person dressed as a koala saying thanks
Jennifer Wong | @mybluewristband

jennz0r.github.io/translating-react