• Ogle
  • Magister
  • Syntheogen
  • nthClock
  • TypeScript
  • JavaScript
  • C++
  • C#
  • React
  • WPF
  • Git
  • Split Notation
  • Physics (PDF)
  • DSP (PDF)
  • Math (PDF)
  • Mathematica
  • Play Time
  • Music
  • Politics
  • “Now this world is so arranged as to be able to maintain itself with great difficulty; but if it were a little worse, it could no longer maintain itself. Consequently a worse world, since it could not continue to exist, is absolutely impossible: thus this world itself is the worst of all possible worlds.”

    The World as Will and Representation, Arthur Schopenhauer

    React Notes

    Front-end web framework notes — React 17

    React Notes

    These are my React notes, covering React 17. React native development is not included here. If you find a mistake, please let me know.

    The example code uses a new notation I am developing. More on that later.

    This page includes a two-column print style. For best results, print in landscape, apply narrow margins, change the Scale setting in your browser’s print options to 70%, and enable background graphics. Firefox and Chrome each have their own set of printing bugs, but one of them usually works.

    Contents

    create-react-app

    create-react-app or CRA is an npm package from Facebook that creates and configures React projects. This single dependency installs and manages a number of other packages, including:

    • Babel, which is used to transpile JSX and newer JavaScript syntax into browser-ready JavaScript;
    • Webpack, which is used to manage module exports and imports, and to bundle imported code into a small number of files that produce fewer requests. It also produces source maps, which correlate transpiled, minified, and bundled code in the browser with unprocessed code, for display when debugging;
    • Jest, which is used to run tests.

    If it becomes necessary to configure these packages directly, CRA can eject the project, converting it to a conventional installation with discrete dependencies. Once ejected, the project cannot be managed again with CRA.

    Creating a project

    To create project name within folder name:

    npx create-react-app name
    

    Running CRA with npx ensures that the latest version is used. The --template switch can be added to select common project configurations:

    cra-template
    The default template.
    cra-template-typescript
    Creates a TypeScript project.
    cra-template-pwa
    Creates a Progressive Web App project.
    cra-template-pwa-typescript
    Creates a Progressive Web App project with TypeScript.

    Many third-party templates are distributed by npm.

    Sources

    Create a New React App

    Running the development build

    To start the development server and run the development build:

    npm start
    

    By default, the build is served to http://localhost:3000/. In most cases, the page reloads automatically when code is updated.

    CRA configures the project with ESLint, which displays warnings in the console that runs npm start, in the VS Code Problems tab, and in the Console tab within the browser DevTools. Specific warnings can be disabled by adding rules properties to the eslintConfig block in package.json:

    "rules": {
      "no-unused-vars": "off"
    }
    

    This disables warnings immediately in VS Code. The warnings are not disabled in the browser until the development server is restarted.

    Running tests

    To run tests:

    npm test
    

    Deploying the project

    To compile the production build:

    npm run build
    

    The build output is stored in the build/ folder. Bundled JavaScript and CSS files are stored in build/static/js/ and build/static/css/; these files are also listed within build/asset-manifest.json, which is compiled automatically. Webpack adds cache-busting hashes to the bundle filenames, and updates these automatically when bundled content changes.

    Development with CRA

    Project structure

    Files in the public/ folder are copied to the build/ folder at compile time. They are not minified, nor are cache-busting hashes added to their names. Unlike src/ files, they cannot be targeted with import; they are meant to be served directly, or referenced from other public/ files with the %PUBLIC_URL% environment variable, which is replaced at compile time with an absolute path:

    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    

    This path is blank by default, so public files are served from the root of the domain. The path can be changed by adding a homepage entry to package.json:

    "homepage": "https://www.anthemion.org/play-ogle"
    

    Note that only the path in this value is reproduced in %PUBLIC_URL%; the protocol, host, and domain are omitted.

    In JavaScript, the public path can be read from process.env.PUBLIC_URL.

    CRA adds these files to public/, among others:

    index.html
    The default page for the app, which is served to the browser before being populated with React output.
    manifest.json
    A basic web app manifest, which stores Progressive Web App metadata to be used when the app is installed on a mobile device. The file is referenced by a manifest link in the index.html head.

    The src/ folder contains index.js, which writes top-level component output to index.html. The folder also stores JavaScript and CSS files that are used to implement components, plus assets referenced by components. All JavaScript imports target files relative to the src/ folder, whether these are modules, CSS files, images, or other imports.

    Sources

    Folder Structure Using the Public Folder Advanced Configuration Add a web app manifest

    Importing modules

    CRA configures Webpack to use ECMAScript module syntax for exports and imports.

    Normally, Node.js interprets files with the .js extension as CommonJS modules. Projects that use ECMAScript modules are expected to use .mjs, or alternatively, to add:

    "type": "module"
    

    to the top level of the package.json file. This is not necessary in CRA projects, because module exports and imports are processed by Webpack. In fact, using the .mjs extension in a CRA project produces confusing compile-time errors like Uncaught TypeError: undefined is not a function and Can't import the named export 'jsxDEV' from non EcmaScript module.

    Note that the React DevTools Components page reads component names from the classes or functions that define them, not from the names by which they may have been imported. For this reason, even default component exports are expected to be named, and ESLint will warn if an anonymous component is exported.

    Code-splitting

    Webpack automatically code-splits modules that are loaded dynamically:

    import("./Mod.mjs").then(aMod => {
      aMod.uStart();
      ...
    });
    

    If a component is loaded this way, it must be the default export of its module, and it must be wrapped on the import side with React.lazy, which accepts a function that performs the import:

    const Grid = React.lazy(() => import("./Grid.mjs"));
    

    The wrapped component can then be nested within a Suspense component, which offers a fallback attribute that displays arbitrary JSX while the component loads:

    const oGrid = (
      <Suspense fallback={<div class="FallLoad"></div>}>
        <Grid />
      </Suspense>
    );
    

    Sources

    Code Splitting

    Importing CSS

    Webpack allows CSS files to be used with import. This adds the referenced file to the CSS bundle, much the way a module import adds to the JavaScript bundle:

    import "./Nav.css";
    

    Though developers often create separate CSS files for each component, the styles in imported files are available throughout the project. They are not specific to the importing module.

    CRA creates src/index.css automatically, and imports it within src/index.js.

    Sources

    Adding a Stylesheet Asset Management

    Importing other files

    Webpack allows images and other files to be used with import. When an image is imported:

    import PathImgLogo from "./ImgLogo.png";
    

    the file is added to the bundle, and a path is returned that can be used to reference it in JSX:

    <img src={PathImgLogo} alt="" />;
    

    For cache-busting purposes, Webpack adds hashes to the paths of bundled files, and changes these automatically when their content changes.

    Webpack also bundles images that are referenced in CSS, potentially inlining them within the CSS itself as base64:

    nav div.Logo {
      background-image: url(./ImgLogo.png);
    }
    

    It is also possible to import JSON data. The file content is parsed automatically and assigned to the import variable:

    import Words from "./Words.json";
    

    Webpack can be configured to import other formats, including XML and CSV.

    Sources

    Adding Images, Fonts, and Files

    JSX

    JSX is a markup language that defines page content declaratively. At compile time, Babel converts JSX blocks into React.createElement expressions, which later generate ReactElement instances. React uses these to update its virtual DOM and, ultimately, the browser content.

    JSX resembles HTML, but it differs in small ways. React uses the DOM API to update page content, so element attributes are set with DOM property names, not HTML attribute names. In particular, element classes are set with className, rather than class. Properties are generally named with camelCase, rather than all lowercase, so onclick becomes onClick. ARIA and HTML data attributes continue to use kebab case, however.

    It is not possible to assign CSS strings to the style attribute in JSX; an object must be embedded instead. CSS properties are identified in this object with camelCased versions of the usual CSS property names:

    const oCSS = {
      color: "white",
      backgroundColor: "black"
    };
    
    return <div style={oCSS}>...</div>;
    

    Empty elements like br must be represented with self-closing tags, even though these are optional in HTML5:

    <br />
    

    JSX blocks are often parenthesized to avoid ASI problems:

    return (
      <h2>Tasks</h2>
    );
    

    Some developers use the jsx file extension for JavaScript files that contain even small amounts of JSX. This does not change how the files are processed.

    Sources

    DOM Elements

    Embedded expressions

    A JavaScript expression can be embedded in JSX by surrounding it with curly braces:

    const oElID = (
      <div>Block {"ID" + oID}</div>
    );
    

    Many expression values are converted automatically to strings; React escapes these and other string values before embedding them. There is no need to use quotes when assigning string results to attributes:

    <div id={oID}>
    

    In fact, quoting the placeholder would cause the braces and the contained expression to be interpreted literally.

    Expressions can return JSX, which is then embedded in the surrounding content. Along with the ternery operator, operators like && are sometimes used to embed conditional JSX expressions:

    <section>
      <div className="CtAct">
        {oCt} active
      </div>
      {oCkWarn &&
        <div className="CtWarn">
          {oTextWarn}
        </div>
      }
    </section>
    

    Embedding an array causes each element to be embedded in sequence. Embedding a non-array object causes an exception to be thrown.

    These values produce no page output when embedded:

    • true
    • false
    • null
    • undefined

    Event handling

    In HTML, an event handler is defined with a string that contains inlined JavaScript. The handler is invoked directly, with parentheses, inside the string:

    <button onclick="uHandReady()">Ready</button>
    

    In JSX, the handler is not invoked; it is passed as a function reference, within a placeholder. Unlike HTML attributes, React events are named with camelCase:

    <button onClick={uHandReady}>Ready</button>
    

    The handler receives a SyntheticEvent instance when it is invoked. This object provides the same interface that a DOM event object would, but it is not the same type. The DOM event object can be obtained from the nativeEvent property within the SyntheticEvent.

    To cancel a button’s default behavior, the handler must call preventDefault. It is not enough to return false:

    function uHandSubmit(aEvt) {
      aEvt.preventDefault();
      ...
    }
    

    If the handler is a method of the component class, it must be bound to the class instance, or this will be defined incorrectly when the handler is invoked through the reference. This can be done by overwriting class methods in the constructor, after wrapping them with bind:

    class Btn extends React.Component {
      constructor(aProps) {
        super(aProps);
        this.uHandClick = this.uHandClick.bind(this);
      }
    
      uHandClick(aEvt) {
        ...
      }
    
      render() {
        return (
          <button onClick={this.uHandClick}>
            {this.props.Text}
          </button>
        );
      }
    }
    

    Handlers can also be wrapped with arrow functions that capture this in a closure:

    this.uHandClick = (aEvt) => this.uHandClick(aEvt);
    

    The wrapper can be defined in the component render function, but this creates a new wrapper every time the component is rendered, which could affect performance:

    <button onClick={(aEvt) => this.uHandClick(aEvt)}>
    

    If a non-event argument is required, that may be unavoidable. If this type of handler is wrapped with an arrow function, the event object must be forwarded in the wrapper definition:

    <button onClick={(aEvt) => this.uHandClickNum(aNum, aEvt)}>
      {aNum}
    </button>
    

    If it is wrapped with bind, this must be forwarded in the wrapper definition, but the event object can be ignored, since a bound function automatically forwards additional arguments to the wrapped function:

    <button onClick={this.uHandClick.bind(this, aNum)}>
      {aNum}
    </button>
    

    Sources

    SyntheticEvent What is JavaScript's CompositionEvent?

    React event handlers

    Handlers can target events in the bubbling phase, or in the capture phase. Commonly-used bubbling event handlers are listed below. The same events can be intercepted during capture by appending Capture to these names.

    Page events
    • onFocus
    • onBlur
    • onScroll
    Form events
    • onChange
    • onInput
    • onInvalid
    • onReset
    • onSubmit
    Text input selection events
    • onSelect
    Mouse events
    • onClick
    • onContextMenu
    • onDoubleClick
    • onDrag
    • onDragEnd
    • onDragEnter
    • onDragExit
    • onDragLeave
    • onDragOver
    • onDragStart
    • onDrop
    • onMouseDown
    • onMouseEnter
    • onMouseLeave
    • onMouseMove
    • onMouseOut
    • onMouseOver
    • onMouseUp
    • onWheel
    Touch events
    • onTouchCancel
    • onTouchEnd
    • onTouchMove
    • onTouchStart
    Pointer events
    • onPointerDown
    • onPointerMove
    • onPointerUp
    • onPointerCancel
    • onGotPointerCapture
    • onLostPointerCapture
    • onPointerEnter
    • onPointerLeave
    • onPointerOver
    • onPointerOut
    Keyboard events
    • onKeyDown
    • onKeyPress
    • onKeyUp
    Clipboard events
    • onCopy
    • onCut
    • onPaste
    CSS transition events
    • onTransitionEnd
    CSS animation events
    • onAnimationStart
    • onAnimationEnd
    • onAnimationIteration
    Media events
    • onAbort
    • onCanPlay
    • onCanPlayThrough
    • onDurationChange
    • onEmptied
    • onEncrypted
    • onEnded
    • onError
    • onLoadedData
    • onLoadedMetadata
    • onLoadStart
    • onPause
    • onPlay
    • onPlaying
    • onProgress
    • onRateChange
    • onSeeked
    • onSeeking
    • onStalled
    • onSuspend
    • onTimeUpdate
    • onVolumeChange
    • onWaiting
    Other events
    • onError
    • onLoad

    Sources

    JSX In Depth

    Components

    React components are reusable structures that generate ReactElement instances, representing page output. Functions in the react-dom module translate these instances to DOM elements in the browser. react-native translates them to native controls in various desktop and mobile platforms.

    React expects component names to be capitalized; names with lowercase initials are assumed to be DOM elements. Components are specified in JSX much the way DOM elements are:

    const oLinks = (
      <ul>
        <ItLink Addr="/sec1" Text="Section 1" />
        <ItLink Addr="/sec2" Text="Section 2" />
        <ItLink Addr="/sec3" Text="Section 3" />
      </ul>
    );
    

    Component definitions must be in scope for the referencing JSX. When their definitions are nested within objects:

    const Graphs = {
      Bar: function (aProps) {
        ...
      },
      Pie: function (aProps) {
        ...
      }
    };
    

    those definitions must be dereferenced with the JavaScript ‘dot’ syntax:

    <div>
      <Graphs.Bar />
      <Graphs.Pie />
    </div>
    

    Component names are always interpreted as function or class references. Component types can be selected conditionally by assigning a reference to a variable, and then using the variable name as a component. The name must be capitalized, however:

    function DispGraph(aProps) {
      const Graph = aProps.CkPie ? Graphs.Pie : Graphs.Bar;
      return (
        <div>
          <Graph />
        </div>
      );
    }
    

    In like manner, when components are passed to functions, the parameter names can be used as components:

    function BtnLbl(Btn, Lbl) {
      return (
        <div>
          <Btn /><Lbl />
        </div>
      );
    }
    

    Function components

    Components can be implemented as functions or classes. A function component resembles the render function found in a class component. It accepts a single props object argument that stores any prop attributes that were assigned when the component was invoked. It returns a JSX expression:

    function ItLink(aProps) {
      return <li><a href={aProps.Addr}>{aProps.Text}</a></li>;
    }
    

    or an array of JSX expressions, to be rendered in sequence within the component’s parent element:

    function ItsLink(aProps) {
      return [
        <ItLink key="status" Addr="/status" Text="Status" />,
        <ItLink key="updates" Addr="/updates" Text="Updates" />,
        <ItLink key="settings" Addr="/settings" Text="Settings" />
      ];
    }
    

    If an array is returned, a sequence-unique key property should be assigned to each element. This value is not added to the component props. React uses keys to track changes within the sequence, so element indices should not be used as key values if elements can be deleted, or if the sequence can be reordered.

    Keys must be assigned in the array itself:

    <ul>
      {oNums.map(o => <It key={o.toString()} Num={o} />)}
    </ul>
    

    They cannot be assigned in the embedded component’s render function.

    The component can return null if it generates no page content. Like other render functions, function components should not produce side effects.

    Class components

    Class components subclass React.Component:

    import React from "react";
    
    class ItLink extends React.Component {
      constructor(aProps) {
        super(aProps);
        ...
      }
    
      render() {
        return (
          <li>
            <a href={this.props.Addr}>{this.props.Text}</a>
          </li>
        );
      }
    }
    

    Class instances are created and managed by React, which supplies the props object as a constructor argument. If a custom constructor is defined, it must forward this parameter to the base constructor, which stores it in the props class property.

    The subclass must define a render method. This returns a JSX expression, an array of JSX expressions, or null, just as a function component would.

    The constructor and the render method may be called at any time during the render phase, so they should not produce side effects. Operations with side effects should be confined to the componentDidMount, componentDidUpdate, and componentWillUnmount methods. If the component must subscribe to an event published by another object, it can do so within componentDidMount.

    Though props are passed to the constructor, props changes do not cause class components to be reinstantiated. They do cause the component to be re-rendered, and props is updated automatically before render is called.

    Sources

    React.Component

    Component props

    Most attributes assigned to component instances:

    <ItLink Addr="/" Text="Home" />
    

    are combined into a props object that is forwarded to the component constructor and used to configure its output. This object is frozen, so properties cannot be added or changed.

    A prop can be assigned any value, including a function or object reference. If an attribute is specified in JSX without a value, the corresponding prop will be set to true:

    <BtnRadio CkDown />
    

    It is better to set these values explicitly, however:

    <BtnRadio CkDown={true} />
    

    The spread syntax can be used to forward the properties of an object as distinct props:

    function ItLink(aProps) {
      return <li><Link {...aProps}/></li>
    }
    

    Default props

    Ordinarily, if a prop’s attribute is not assigned when the component is invoked, it will be undefined in the component’s render function. Default values can be specified by assigning an object to the component’s defaultProps property, however:

    function Ship(aProps) {
      return <div>...</div>
    }
    
    Ship.defaultProps = {
      CdLoc: "PEND",
      CtFail: 0,
      CtRetry: 0
    };
    

    Prop type checking

    React can also perform run-time type and value checking on component props. Type checks are defined by assigning an object to the component’s propTypes property. For each check that fails, a warning will be logged to the console. Checks are performed only during the development build:

    import PropTypes from "prop-types";
    
    function StatWare(aProps) {
      return (
        <section>
          <h3>Area {aProps.NumArea}</h3>
          <div>{aProps.Notes}</div>
        </section>
      );
    }
    
    StatWare.propTypes = {
      NumArea: PropTypes.number,
      Notes: PropTypes.string
    };
    

    The propTypes object contains properties that associate prop names with validators imported from the prop-types module:

    bool number string symbol object array func
    Matches any value with the specified JavaScript type.
    node
    Matches any type that can be rendered by React, including numbers, strings, and elements, plus arrays or fragments of such.
    element
    Matches a ReactElement instance.
    elementType
    Matches any reference to a React component.
    instanceOf(class)
    Matches an object that inherits, directly or indirectly, from class.
    oneOf(vals)
    Matches any value within array vals. The vals elements can vary in type.
    oneOfType(types)
    Matches any of the PropTypes validators in array types.
    arrayOf(type)
    Matches an array if all of its elements match the given PropTypes validator.
    objectOf(type)
    Matches an object if all of its properties match the given PropTypes validator.
    shape(patt)
    Matches an object if none of its properties conflict with object patt, which maps property names to PropTypes validators. Extra object properties are ignored, as are missing properties.
    exact(patt)
    Functions as shape, but rejects objects that have extra properties. Missing properties are still ignored.

    Note that complex validators like arrayOf can be used within other validators, such as oneOfType:

    StatWare.propTypes = {
      // Allow a single number or an array of strings:
      Data: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.arrayOf(PropTypes.string)
      ]),
      NumArea: PropTypes.number,
      Notes: PropTypes.string
    };
    

    All validators define an isRequired property that acts as the same validator, while also warning if the associated property is undefined:

    StatWare.propTypes = {
      NumArea: PropTypes.number.isRequired,
      ...
    };
    

    Custom validation functions can also be associated with props. Each function accepts a props object, a string containing the name of the prop being validated, and a string that gives the component name. The function should return an Error instance if the prop is invalid:

    function Valid_PropNumShelf(aProps, aNameProp, aNameCpt) {
      if (aProps.NumShelf
        && ((aProps.NumShelf < 1) || (aProps.NumShelf > NumShelfMax)))
        return new Error(aNameCpt + ": Invalid shelf number")
    }
    
    StatWare.propTypes = {
      NumShelf: Valid_PropNumShelf,
      ...
    };
    

    Custom functions can also be passed to PropTypes.arrayOf or PropTypes.objectOf. These functions are invoked once for each array element or object property. They accept the props object, the index or key of the value being checked, the component name, an undocumented location parameter, and the full name of the property or element being checked.

    Props that are not named in the propTypes object are not checked. Default prop values are not validated unless they are used by the component.

    Sources

    Typechecking With PropTypes

    Component state

    A component’s props are defined in the JSX or other code that causes it to be instantiated. They are immutable within the component, so it cannot trigger updates by modifying them itself. If a control changes in response to user input, it must trigger an update by modifying its state.

    A class component’s state is stored in an object referenced by the state property, defined by React.Component, from which the class inherits. This object can be assigned in the constructor, but direct updates are disallowed thereafter. Instead, updates are performed with the setState method, which is also inherited from React.Component. This method eventually causes state to be updated, which causes render to be invoked:

    class Page extends React.Component {
      constructor(aProps) {
        super(aProps);
        this.state = {CkAlert: false};
        this.uShow_Alert = this.uShow_Alert.bind(this);
      }
    
      uShow_Alert() {
        this.setState({CkAlert: true});
      }
    
      uBoxAlert() {
        if (!this.state.CkAlert) return null;
        ...
      }
    
      render(aProps) {
        return (
          <div>{this.uBoxAlert()}</div>
        );
      }
    }
    

    When an object is passed to setState, it is merged with the current state data; properties in the new state are made to overwrite those in the old, while properties in the old that were not specified in the new are left as-is.

    The state update is asynchronous, so the new state object should not derive values from the current state, which may be out of date when the update occurs. If it is necessary to derive new from old, an update function can be passed instead:

    this.setState((aState, aProps) => ({
      IDNext: aState.IDNext + 1
    }));
    

    This function accepts parameters representing the original state and the component props. It returns new state data, to be merged with the current state. It is invoked during the render phase, and it may be called more than once, so it should not produce side effects.

    setState also accepts a callback as an optional second argument, to be invoked after the component is re-rendered.

    While component updates are usually triggered by state changes, or by changes in the component’s parent, an instance can made to re-render by calling its forceUpdate method. This causes the shouldComponentUpdate lifecycle event to be skipped. It accepts an optional callback argument that is invoked after the forced update.

    Sources

    What does the callback in forceUpdate do?

    Component children

    When elements are nested within a component’s start and end tags in JSX:

    <Sidebar Head="Common problems">
      <ul>
        <li>Uninitialized pointers</li>
        <li>Null pointers</li>
        <li>Dangling pointers</li>
      </ul>
    </Sidebar>
    

    they are automatically assigned to the component’s children prop. This allows those children to be embedded within the component’s output:

    function Sidebar(aProps) {
      return (
        <section className="Sidebar">
          <header>
            <h3>{aProps.Head}</h3>
          </header>
          {aProps.children}
        </section>
      );
    }
    

    It is also possible to pass non-JSX values as children, including functions and other objects. These are then referenced in the component by the children prop, just as a JSX expression would be. The React.Children object provides utility functions like React.Children.map that process these and other child elements.

    If the containing component consumes multiple JSX expressions, they can be explicitly assigned to props:

    <BoxCompare
      OptA={
        <div>
          <h2>Manual checks</h2>
          <p>Error-prone</p>
          <p>Verbose</p>
        </div>
      }
      OptB={
        <div>
          <h2>Automated checks</h2>
          <p>Resource-intensive</p>
          <p>Less flexible</p>
        </div>
      }
    />
    

    and then read from the props at render time:

    function BoxCompare(aProps) {
      return (
        <section className="BoxCompare">
          {aProps.OptA}
          <hr />
          {aProps.OptB}
        </section>
      );
    }
    

    As usual, each expression must define a single parent element; otherwise, fragments or JSX arrays must be passed.

    Render props

    The children prop allows the structure defined by one component to be reused with different children. A render prop provides similar functionality, while also allowing the component to pass data to those children.

    The render prop is an ordinary component prop, to which a function has been assigned. The prop can have any name, but it is conventional to call it render:

    <Spin render={
      aVal => (<div>Current: {aVal}</div>)
    }/>
    

    Note that defining the prop function within JSX causes a new function instance to be created each time the JSX is evaluated. That could affect performance, and it should not be combined with React.PureComponent, which uses reference equality to detect prop changes.

    The function accepts whatever arguments the containing component wishes to provide, and returns a JSX expression to be embedded by the component:

    class Spin extends React.Component {
      constructor(aProps) {
        super(aProps);
        this.state = {Val: 0};
        this.uDec = this.uDec.bind(this);
        this.uInc = this.uInc.bind(this);
      }
    
      uDec(aEvt) {
        this.setState(aState => ({Val: aState.Val - 1}));
      }
    
      uInc(aEvt) {
        this.setState(aState => ({Val: aState.Val + 1}));
      }
    
      render() {
        return (
          <div>
            {this.props.render(this.state.Val)}
    
            <button onClick={this.uDec}>-1</button>
            <button onClick={this.uInc}>+1</button>
          </div>
        );
      }
    }
    

    Fragments

    Only one top-level element can be returned by a component. To return multiple elements without a container, wrap them in React.Fragment:

    return (
      <React.Fragment>
        <div>LAND C</div>
        <div>LAND X</div>
      </React.Fragment>
    );
    

    This can also be written as:

    return (
      <>
        <div>LAND C</div>
        <div>LAND X</div>
      </>
    );
    

    however, only the full React.Fragment syntax allows key attributes to be assigned to fragments, if the fragments themselves happen to be part of an array. key is the only attribute that can be assigned to a fragment.

    Component context

    Ordinarily, component configuration data is passed from ancestor elements to descendents via props, but this can be verbose when elements are deeply nested. The React context system allows data to be shared with descendents without forwarding props at each level.

    Context data is stored within a context object, created with React.createContext. At render time, JSX will be used to assign values to this context, and descendents that subscribe to the context will read from it. React.createContext accepts a single argument that sets the default value, to be read when no value has been assigned by an ancestor in the JSX:

    const ContextStat = React.createContext("Red");
    

    The context object defines a Provider component with a value attribute that sets the context value:

    <Box />
    <ContextStat.Provider value="Green">
      <Box />
      <ContextStat.Provider value="Blue">
        <Box />
      </ContextStat.Provider>
    </ContextStat.Provider>
    

    This value is available to all components contained by the Provider, regardless of depth. When providers are nested, each value takes precedence over the ones above it. Subscribing components are updated when a value changes, even if their shouldComponentUpdate methods return false.

    By default, in the React DevTools Components page, all provider components are listed as Context.Provider, regardless of the context object that defines them. The displayName property within the context object can be used to replace Context with a distinct name:

    ContextStat.displayName = "ContextStat";
    

    A class component subscribes to the context by assigning the context object to a class-static variable named contextType. At render time, it reads the current value from its own context property:

    class Box extends React.Component {
      static contextType = ContextStat;
    
      render() {
        return (
          <div className={`Box ${this.context}`}>
            PEND
          </div>
        );
      }
    }
    

    In the past, a function component subscribed to the context by embedding the Consumer component defined within the context object. This component interprets its child as a render prop, which itself receives the current context value as an argument, and returns the content to be displayed within the Consumer:

    function Box(aProps) {
      return (
        <ContextStat.Consumer>
          {aStat => (
            <div className={`Box ${aStat}`}>
              PEND
            </div>
          )}
        </ContextStat.Consumer>
      );
    }
    

    Now, function components can read context values by calling the useContext hook. This allows context data to be used without passing a render prop.

    By calling useContext more than once (or by embedding multiple Consumer components) a function component can subscribe to multiple contexts. Class components cannot subscribe to more than one, so the context must store an object if multiple values are to be shared:

    const ContextStat = React.createContext({Sty: "Red", Text: "PEND"});
    
    class Box extends React.Component {
      static contextType = ContextStat;
    
      render() {
        return (
          <div className={`Box ${this.context.Sty}`}>
            {this.context.Text}
          </div>
        );
      }
    }
    

    However, React compares objects by reference when checking for changes. If provider values are assigned in JSX with object literals:

    <ContextStat.Provider value={{Sty: "Green", Text: "READY"}}>
    

    new value objects will be created every time the JSX is evaluated, the comparison will always fail, and unnecessary DOM updates will result. To avoid this, value objects should be stored in the component state, and referenced from the Provider element:

    class App extends React.Component {
      constructor(aProps) {
        super(aProps);
        this.state = {
          StatReady: {Sty: "Green", Text: "READY"},
          StatAct: {Sty: "Blue", Text: "ACT"}
        };
      }
    
      render() {
        return (
          <div className="App">
            <header className="App-header">
              <Box />
              <ContextStat.Provider value={this.state.StatReady}>
                <Box />
                <Box />
                <ContextStat.Provider value={this.state.StatAct}>
                  <Box />
                </ContextStat.Provider>
              </ContextStat.Provider>
            </header>
          </div>
        );
      }
    }
    

    Sources

    Context

    Element refs

    Ordinarily, page content is modified by passing new props to components, or by changing their state, causing them to be re-rendered. The React element tree then updates the DOM content. Parent components cannot call child component methods directly, because component instances are created and managed by React. Nor can parents access DOM element instances.

    A React ref provides direct access to a React component or DOM element instance. Ref objects are created with React.createRef. This function accept no arguments, and is often called in a component constructor:

    class InpName extends React.Component {
      constructor(aProps) {
        super(aProps);
        this.RefInp = React.createRef();
        this.uFocus_Inp = this.uFocus_Inp.bind(this);
      }
      ...
    

    At this point, the object references nothing. It is associated with a component or DOM element by assigning it to the target’s ref attribute during rendering:

    render() {
      return <input name="Name" ref={this.RefInp} />;
    }
    

    The component or DOM element instance is then accessible through the current property in the ref object:

    uFocus_Inp() {
      this.RefInp.current?.focus();
    }
    

    Until recently, function components were stateless, so they could not define refs. They can define them now with ref hooks.

    Callback refs

    A reference to a component or DOM element can also be obtained by assigning a callback function to the ref attribute:

    <input name="Name" ref={this.uSet_ElInp} />
    

    The callback is invoked when the component is mounted or unmounted. It accepts a single parameter that references the new element, or null, as appropriate:

    uSet_ElInp(aEl) {
      this.ElInp = aEl;
    }
    

    If the callback is a class method, it should be bound to this, like other event handlers:

    constructor(aProps) {
      super(aProps);
      this.uSet_ElInp = this.uSet_ElInp.bind(this);
      this.uFocus_Inp = this.uFocus_Inp.bind(this);
    }
    

    The reference itself is passed to the callback, so there is no current property:

    uFocus_Inp() {
      this.ElInp?.focus();
    }
    

    Forwarded refs

    A parent component cannot associate a ref attribute with a component or DOM element unless the target is defined in the parent’s render function. The component that does render the target can forward a ref it has received, however, allowing the parent to reference an element it does not render directly.

    React.forwardRef creates a special function component that forwards a ref to one of its own children. It accepts a callback that itself resembles a function component; the callback accepts props and ref arguments, and returns component content. Within the callback, the ref is assigned to the target’s ref attribute as usual. React.forwardRef then returns the forwarding component:

    const InpDock = React.forwardRef((aProps, aRef) => (
      <input name={"InpDock" + aProps.ID} ref={aRef} />
    ));
    
    class BoxDoc extends React.Component {
      constructor(aProps) {
        super(aProps);
        this.RefInp = React.createRef();
        this.uReady = this.uReady.bind(this);
      }
    
      uReady() {
        this.RefInp.current?.focus();
        ...
      }
    
      render(aProps) {
        return (
          <div>
            <InpDock ref={this.RefInp} />
            ...
          </div>
        );
      }
    }
    

    Class components cannot be passed to React.forwardRef. They can be wrapped in a function component that forwards the reference through a prop, however. The forwarding prop cannot be named ref, as that attribute is handled specially by React:

    const InpDock = React.forwardRef((aProps, aRef) => (
      <InpDockBase {...aProps} RefForw={aRef} />
    ));
    

    The forwarding prop can then be assigned to ref in the underlying class component:

    class InpDockBase extends React.Component {
      render() {
        return (
          <input name={"InpDock" + this.props.ID} ref={this.props.RefForw} />
        )
      }
    }
    

    Sources

    Refs and the DOM Forwarding Refs How to use React.forwardRef in a class based component?,

    Lifecycle methods

    Various lifecycle methods are invoked on class components as they are added to the page, updated, and later removed. Implementing these methods allows the component to perform special handling during these events.

    Component mounting

    A component is said to be mounted after it is first added to the page. The following methods are invoked during and after mounting:

    • The component constructor;
    • The static getDerivedStateFromProps method, which allows components to modify their state in response to props changes;
    • The render method;
    • The componentDidMount method. Side effects are allowed within this method.

    Component updates

    The following methods are invoked during the update phase, in response to props or state changes:

    • The static getDerivedStateFromProps method;
    • The shouldComponentUpdate method, which allows a component to bypass an update if the props or state change that triggered it does not affect its output;
    • The render method;
    • The getSnapshotBeforeUpdate method, which is invoked before changes are reflected in the DOM, allowing components to collect information about the previous DOM state;
    • The componentDidUpdate method. Side effects are allowed within this method.

    Note that props changes cause the component to be re-rendered, but do not cause its class to be reinstantiated.

    Component unmounting

    A component is said to be unmounted after it is removed from the page. One method is invoked when unmounting:

    • The componentWillUnmount method. Side effects are allowed within this method.

    Higher-order components

    A higher-order component or HOC is a function that receives a component as an argument, and returns a new component:

    // Returns a new component that renders component 'Child',
    // with the result of function 'auSrcData' assigned to that
    // component's 'Data' prop. Invoke method 'uUpd' in the new
    // component to fetch new 'Data' and update:
    function ConsumData(Child, auSrcData) {
      return class extends React.Component {
        constructor(aProps) {
          super(aProps);
          this.state = {Data: auSrcData()};
        }
    
        uUpd() {
          this.setState(aState => ({Data: auSrcData()}));
        }
    
        render() {
          return <Child Data={this.state.Data} {...this.props} />;
        }
      };
    }
    

    This can be used to compose functionality. The child component is passed to the HOC, along with any other arguments it might need. The HOC can share state data with its child by assigning a prop. Other props are forwarded to the child with the props spread syntax.

    The HOC result is stored in a variable:

    function BoxLot(aProps) {
      return <div>...</div>
    }
    
    function uDataLot() {
      return {...}
    }
    
    const Lot = ConsumData(BoxLot, uDataLot);
    

    and then used like any other component:

    <div>
      <Lot />
      ...
    

    If the HOC were invoked within another component’s render method, a new class would be created with each update, and performance would suffer.

    It is particularly easy to define an HOC if the containing component has already been implemented with a render prop:

    class BridgeData extends React.Component {
      constructor(aProps) {
        super(aProps);
        this.state = {Data: aProps.uSrcData()};
      }
    
      uUpd() {
        this.setState(aState => ({Data: this.props.uSrcData()}));
      }
    
      render() {
        return this.props.render(this.state.Data);
      }
    }
    

    The HOC simply returns a new function component that embeds the child within the forwarded render function:

    function ConsumData(Child, auSrcData) {
      return aProps => (
        <BridgeData uSrcData={auSrcData} render={
          aData => (<Child Data={aData} {...aProps} />)
        } />
      );
    }
    

    Whereas the containing component in the first example passes data to the child by embedding it as a prop within its JSX, the container in the second passes it as an argument to the supplied render prop function, which embeds it within its own JSX.

    Note that ref assignments look like props, but they are handled specially by React. If an ordinary ref is assigned to an HOC, it will come to reference the HOC itself, not the child component. If necessary, the HOC can use React.forwardRef to forward the ref to its child.

    Sources

    Higher-Order Components

    Performance optimization

    As will be seen, ReactDOM.render is used to embed React content within one or more container elements in the page. For each container, React maintains an abstract representation of the content called the virtual DOM or VDOM. During the render phase, component render functions are invoked to produce a new virtual DOM tree, which React compares against the existing virtual DOM. The following functions may be called one or more times during the render phase, so they should not produce side effects:

    • Component constructors;
    • getDerivedStateFromProps;
    • State update functions passed to setState;
    • shouldComponentUpdate;
    • Component render functions.

    During the commit phase, React updates the browser DOM to reflect any changes it detected, then it stores the new virtual DOM. The process as a whole is called reconciliation.

    By default, a component is re-rendered if its props or state change. Re-rendering a parent also causes its children to be re-rendered, even if their props and state are unchanged. To improve performance, class components can override the shouldComponentUpdate method and return false if neither they nor their children should re-render for a given update. Class components can also subclass React.PureComponent, in place of React.Component. Before re-rendering, this class automatically compares values within the props and state to determine whether their content actually changed. Arrays and other objects are compared by reference, so only top-level values are considered. Function components can be wrapped with the React.memo HOC, which performs a similar props comparison before reinvoking the component function:

    function Btn(aProps) {
      ...
    }
    export default React.memo(Btn);
    

    Sources

    Reconciliation Virtual DOM and Internals React (Virtual) DOM Terminology

    Forms

    Controlled components

    Once displayed in the page, most elements change their appearance and behavior only in response to DOM operations. In React, this entails a change to the component’s props or state, which causes it to be re-rendered, and the DOM to be updated.

    Form inputs respond to DOM operations, but they also respond directly to user input, even though their props and state have not changed. To make their state management consistent with other React elements, form inputs can be implemented as controlled components, which are explicitly backed by React state data. In this context, the word component may refer to a React component, or to a DOM element.

    Within render, each controlled component reads its value from the form state. It also assigns an onChange handler that updates the form state to match new, user-entered values. It is this update — rather than the user’s input — that causes the new value to persist within the control. This is called one-way data binding:

    class FormAddLot extends React.Component {
      constructor(aProps) {
        super(aProps);
    
        this.state = {
          IDLot: "000",
          CkBypass: false
        };
    
        this.uHandChange = this.uHandChange.bind(this);
        this.uHandSubmit = this.uHandSubmit.bind(this);
      }
    
      uHandChange(aEvt) {
        const oEl = aEvt.target;
        const oVal = (oEl.type === "checkbox") ? oEl.checked : oEl.value;
        const oState = {[aEvt.target.name]: oVal};
        this.setState(oState);
      }
    
      uHandSubmit(aEvt) {
        aEvt.preventDefault();
    
        const oDataLot = {
          IDLot: this.state.IDLot,
          Mode: this.state.Mode,
          CkBypass: this.state.CkBypass
        };
        ...
      }
    
      render() {
        return (
          <form onSubmit={this.uHandSubmit}>
            <div><label>Lot ID
              <input name="IDLot" value={this.state.IDLot}
                onChange={this.uHandChange} />
            </label></div>
    
            <div><label>Mode
              <select name="Mode" value={this.state.Mode}
                onChange={this.uHandChange}>
                <option value="Ready">Ready</option>
                <option value="Stand">Stand</option>
              </select>
            </label></div>
    
            <div><label>Bypass
              <input name="CkBypass" type="checkbox"
                checked={this.state.CkBypass}
                onChange={this.uHandChange} />
            </label></div>
    
            <input type="submit" value="Add" />
          </form>
        );
      }
    }
    

    If the onChange handler were omitted, the form state would go unchanged, and the user’s input would be overwritten with the default value, making the control effectively read-only. Controls set to null or undefined values in render are always editable, however.

    Note that most input values are set with the value attribute in JSX, including elements like select and textarea, which specify values differently in HTML. Assigning an array to value selects multiple options, in controls that support multi-select. Checkbox values are set with checked.

    Also, in HTML, an input is associated with a label by defining it as a child element, or by referencing it with the for label attribute. In JSX, for is replaced by htmlFor.

    Uncontrolled components

    Uncontrolled components are not backed by React state data; instead, React refs are used to reference DOM elements within the form, and the DOM API is used to read their values:

    class FormCatAdd extends React.Component {
      constructor(aProps) {
        super(aProps);
        this.RefInpCd = React.createRef();
        this.RefInpName = React.createRef();
        this.uHandSubmit = this.uHandSubmit.bind(this);
      }
    
      uHandSubmit(aEvt) {
        aEvt.preventDefault();
    
        const oDataCat = {
          Cd: this.RefInpCd.current?.value,
          Name: this.RefInpName.current?.value
        };
        ...
      }
    
      render() {
        return (
          <form onSubmit={this.uHandSubmit}>
            <div><label>Code:
              <input type="text" defaultValue="A0" ref={this.RefInpCd} />
            </label></div>
    
            <div><label>Name:
              <input type="text" ref={this.RefInpName} />
            </label></div>
    
            <input type="submit" value="Add" />
          </form>
        );
      }
    }
    

    In React, the defaultValue attribute can be used to display a default value when the form appears, without overwriting the user’s input, as value does. Checkbox and radio button defaults can be set with defaultChecked.

    File inputs do not allow their values to be set with the DOM API, so they must be implemented as uncontrolled components.

    Sources

    Forms Uncontrolled Components

    Page output

    React.createElement

    All React output is ultimately produced by React.createElement. JSX in particular is translated at compile time to nested invocations of this method:

    React.createElement(type, [props], [...children])
    Returns an immutable ReactElement instance. The content is defined by type, which references a React component, or a string that contains an HTML tag name. props can be set to an object that specifies the props for the new element, or null or undefined if no props are defined. One or more ReactElement instances can be passed as children, or a single array of these can be passed. The children instances become children of the new element.

    When children references an array, each child must define a unique key value:

    const oElA = React.createElement("div", {key: "A"}, "Area A");
    const oElB = React.createElement("div", {key: "B"}, "Area B");
    return React.createElement(React.Fragment, {}, [oElA, oElB]);
    

    This is not necessary when children are passed as separate arguments:

    const oElA = React.createElement("div", {}, "Area A");
    const oElB = React.createElement("div", {}, "Area B");
    return React.createElement(React.Fragment, {}, oElA, oElB);
    

    Sources

    React Top-Level API

    ReactDOM.render

    React output is typically embedded in the page with ReactDOM.render:

    ReactDOM.render(elem, contain, [call])
    Causes ReactElement instance elem and its children to replace the content of DOM element contain. If function call is provided, it will be invoked after contain is updated.

    Though they can define any number of separate container elements, pages often have a single root element:

    <div id="Root"></div>
    

    that contains the entire React app:

    const oElApp = (
      <React.StrictMode>
        <App />
      </React.StrictMode>
    );
    
    ReactDOM.render(
      oElApp,
      document.getElementById("Root")
    );
    

    A root-level React component can be removed from the page by calling ReactDOM.unmountComponentAtNode, which accepts a single argument that gives the DOM element that contains the component.

    Sources

    Add React to a Website ReactDOM Strict Mode

    Portals

    Ordinarily, a component’s output is written to its position in the element hierarchy. Any elements it generates become children of the component’s parent.

    A portal can be used to direct output to a DOM element other than the component’s parent. The component invokes ReactDOM.createPortal within its render function, and returns the resulting portal instance. This method is invoked much like ReactDOM.render:

    ReactDOM.createPortal(elem, contain)
    Creates and returns a portal object that causes ReactElement elem to be rendered into DOM element contain, rather than the parent of the invoking component. elem and its children replace the content of contain.

    By invoking ReactDOM.createPortal conditionally, a component can send its output to a portal, or to the component’s parent, as usual:

    const ElDlg = document.getElementById("Dlg");
    
    function SecMsg(aProps) {
      const oOut = (
        <section>
          <h3>{aProps.Head}</h3>
          <div>
            {aProps.Msg}
          </div>
        </section>
      );
    
      if (aProps.CkDlg) return ReactDOM.createPortal(oOut, ElDlg);
      return oOut;
    }
    

    Many DOM events bubble up through the element hierarchy until stopPropagation is invoked on the event. React propagates its own SyntheticEvent instances through the React element tree, and these events continue to move through the hierarchy that contains the portal-using control, even though the control’s output has been redirected to a different hierarchy within the DOM.

    Sources

    Portals

    Error boundaries

    By default, when a component throws an exception from its constructor, its render method, or a lifecycle method, the component and its content are removed from the page.

    An error boundary is a special component that catches these exceptions when they are thrown from contained components, allowing errors to be logged, and fallback content to be displayed. A class component will act as an error boundary if it defines one or both of getDerivedStateFromError and componentDidCatch. Function components cannot serve as boundaries.

    getDerivedStateFromError is a class-static method that is called by React when a contained component throws. It receives the exception as a parameter, and returns an object that React uses to update the component state. Often, this state object is used to set a property that the render method uses to display fallback content:

    static getDerivedStateFromError(aErr) {
      return {CkErr: true};
    }
    

    componentDidCatch is a component class method that is called by React after getDerivedStateFromError. It receives the exception as an argument, plus an object containing a componentStack property that stores a stack trace:

    componentDidCatch(aErr, aInfoErr) {
      Log.Add(aErr, aInfoErr);
    }
    

    Note that development builds display exception text and stack traces in the page even if those exceptions were caught by error boundaries. To see the page as it would be rendered by a production build, press Esc.

    Error boundaries do not catch exceptions thrown from DOM event handlers or asynchronous functions. Event handler exceptions do not cause the component’s content to be removed from the page, however.

    Sources

    Error Boundaries

    Hooks

    Function components produce no component instances, so they are traditionally unable to maintain component state, or use stateful features like refs. React 16.8 introduced hooks, which are special functions that add these abilities to function components.

    Several rules apply to hook usage. CRA installs an ESLint plugin eslint-plugin-react-hooks that checks some of these at compile time:

    • Hooks are usable only within function components, or within custom hook functions. They cannot be used in class components;
    • Hooks that are called once must be called every time the component is rendered, and in the same order. For this reason, they may not be called conditionally, or from event handlers, nor may the containing function place a conditional return before any hook. eslint-plugin-react-hooks also prevents hooks from being called within loops, even when the iteration count is fixed. It is also recommended that hooks not be called within nested functions.

    When a hook is invoked from a function component, it creates or modifies state data that is specific to the component instance. When the component instance is rendered again, the same hook is made to reference the same data.

    Sources

    Introducing Hooks Hooks at a Glance Rules of Hooks Hooks API Reference Hooks FAQ

    State hooks

    State hooks are created with the useState function within the react package. They allow function components to manage state, much the way class components do with setState:

    import React, { useState } from "react";
    

    Each useState call defines a single state variable, which can store a primitive or an object:

    function Spin(aProps) {
      const [oVal, ouSet_Val] = useState(aProps.ValDef | 0);
    
      function ouInc(aEvt) { ouSet_Val(aVal => (aVal + 1)); }
      function ouDec(aEvt) { ouSet_Val(aVal => (aVal - 1)); }
    
      return (
        <div>
          <div>{oVal}</div>
          <button onClick={ouDec}>-1</button>
          <button onClick={ouInc}>+1</button>
        </div>
      );
    }
    

    useState accepts a starting value for the variable, or a function that returns such a value. If a function is passed, it is invoked only once, when useState is first called. This can be used to avoid lengthy operations that would otherwise be repeated every time the containing function component re-renders.

    useState returns an array containing the current value, plus a function that can be used to update the value. This update function completely replaces state objects; it does not merge them as setState does. It can accept a new value, or a callback that accepts the current value and returns the new one. To avoid race conditions, the callback should be used if the new value derives from the old.

    Calling useState more than once produces multiple state variables; React uses the order of these calls to link each with its particular allocation. It is recommended that values or objects that vary independently be managed with separate useState calls, rather than combining them into a single object.

    When the component is selected in the React DevTools Components tab, its hooks and their associated data are listed in the hooks section within the component properties.

    Sources

    Using the State Hook

    Reducer hooks

    Reducer hooks can be used to manage state in components with complex state transitions.

    A reducer is a pure function that accepts an object representing the current state, plus a second action argument that represents a state transition. The reducer uses these to return a new object representing the next state.

    useReducer can be called with two arguments:

    1. A reducer;
    2. An object that defines the initial state of the component.
    or three:
    1. A reducer;
    2. Any value;
    3. A function that accepts the second argument, and returns the initial state.

    useReducer returns an array containing the current state object, plus a dispatcher. This function accepts an action argument and forwards it to the reducer, which triggers the next state transition:

    import React, { useReducer } from "react";
    
    function uStSpinNext(aSt, aAct) {
      switch (aAct.Type) {
        case "DEC":
          return {Val: aSt.Val - aAct.Qty}
        case "INC":
          return {Val: aSt.Val + aAct.Qty}
        default:
          throw Error("uStSpinNext: Invalid action type");
      }
    }
    
    function Spin(aProps) {
      const oStInit = {Val: aProps.ValDef | 0};
      const [oSt, ouDispatSt] = useReducer(uStSpinNext, oStInit);
    
      function ouDec1(aEvt) { ouDispatSt({Type: "DEC", Qty: 1}); }
      function ouDec10(aEvt) { ouDispatSt({Type: "DEC", Qty: 10}); }
    
      function ouInc1(aEvt) { ouDispatSt({Type: "INC", Qty: 1}); }
      function ouInc10(aEvt) { ouDispatSt({Type: "INC", Qty: 10}); }
    
      return (
        <div>
          <div>{oSt.Val}</div>
          <button onClick={ouDec10}>-10</button>
          <button onClick={ouDec1}>-1</button>
          <button onClick={ouInc1}>+1</button>
          <button onClick={ouInc10}>+10</button>
        </div>
      );
    }
    

    useReducer returns the same dispatcher instance each time, so the dispatcher can be assigned as a prop without producing unnecessary updates. This allows child components to trigger state transitions.

    Effect hooks

    Like all render functions, function components are prohibited from producing side effects, at least directly. Side effects include DOM changes, network requests, and even logging.

    Effect hooks provide a way to run code that produces side effects from function components, serving much as componentDidMount, componentDidUpdate, and componentWillUnmount do in class components.

    useEffect accepts an effect function. React will call this function some time after the component has been rendered, and the browser repainted, so it can produce side effects:

    import React, { useState, useEffect } from "react";
    
    function StatPress(aProps) {
      const [oData, ouSet_Data] = useState(null);
    
      // Subscribes this component to the status data publisher,
      // causing pressure data to be forwarded to 'ouSet_Data'.
      // Returns a function that unsubscribes it:
      function ouEffSubData() {
        const ouSub = aData => ouSet_Data(aData);
        PubDataStat.Add_Sub(CdStatPress, ouSub);
        return () => PubDataStat.Rem_Sub(CdStatPress, ouSub);
      }
      useEffect(ouEffSubData, []);
    
      if (!oData) return "PEND";
    
      return (
        <section>
          <h2>Pressure</h2>
          <div>Current: {oData.Curr}</div>
          ...
      );
    }
    

    The effect function accepts no parameters. It can return a zero-parameter cleanup function; if it does, React will call this function before the effect function is called again, and also before the component is unmounted.

    By default, React calls the effect after every render, and the cleanup before every effect after the first. However, useEffect accepts an optional array of state variables or other dependencies by referenceas its second argument. If provided, React stores these values and compares them after each render; neither the cleanup function nor the effect are invoked unless one or more of the values change, with objects being compared by reference. If an empty array is passed, both functions run exactly once: the effect after the first render, and the cleanup before the component unmounts.

    If the effect function uses state variables, directly or indirectly, omitting these from the array may cause the effect to run with obsolete data. For instance, if the effect assigns a handler to the DOM, and if that handler uses a state variable, the used variable must be registered as a dependency. Otherwise, the first handler instance — which recorded the state when useEffect was first called — will persist, even after the state has changed:

      const [oCkPause, ouSet_CkPause] = useState(false);
    
      // Adds a 'keydown' listener to the page. Returns a function
      // that removes the listener:
      function ouEffRegHandKeyDown() {
        function ouHand(aEvt) {
          if ((aEvt.code === "Escape") && oCkPause) ...
        }
        document.addEventListener("keydown", ouHand);
        return () => { document.removeEventListener("keydown", ouHand); }
      }
      useEffect(ouEffRegHandKeyDown, [oCkPause]);
    

    For this reason, ESLint warns that React Hook useEffect has a missing dependency when used variables are omitted.

    Like useState, useEffect can be called more than once in a given component.

    Sources

    Using the Effect Hook

    Layout effect hooks

    Ordinary effect hooks run after the browser has painted the changed component. Sometimes it is necessary to update the DOM manually after rendering, and it is preferable to do this before painting. That can be done with useLayoutEffect, which is identical to useEffect, except that its effect and cleanup functions run before the browser is allowed to paint.

    Context hooks

    In the past, function components used context values by assigning a render prop to the context object’s Consumer component. Context hooks provide a simpler way to read those values.

    The context object is created with React.createContext, as before, and its value is assigned the same way, by embedding the object’s Provider component and setting the associated value prop. However, the function component can now read the value simply by passing the context object to useContext:

    function Box(aProps) {
      const oStat = useContext(ContextStat);
      return <div className={`Box ${oStat}`}>PEND</div>
    }
    

    Ref hooks

    Class components have always used React refs to reference component or DOM element instances that they rendered. It was not originally possible for function components to do this, but they can do so now by using ref hooks. These refs are more general than the element refs produced by React.createRef; they can persist any sort of data, for use by event handlers, or future iterations of the calling function.

    These ref objects are created with useRef. Unlike React.createRef, this function accepts a single argument that determines the starting value of the ref’s current property. As with useState, the same object is returned every time a particular useRef line is executed.

    The current property is made to reference a component or DOM element instance by assigning it to the target’s ref attribute. As usual, this value is not set until the first time the component is rendered:

    function PanLog(aProps) {
      const oRefBtn = useRef(null);
    
      function uReady() {
        oRefBtn.current?.focus();
        ...
      }
      useEffect(uReady, []);
    
      return (
        <div>
          <button ref={oRefBtn}>Reset</button>
          ...
      );
    }
    

    The current property can also be set manually to any value, as can any other property in the ref object. The result is something like a static local variable. Unlike component props or state, changing such a value does not cause the component to be re-rendered.

    Customizing element ref output

    By default, when a ref is assigned to an element’s ref attribute, its current property is made to reference that component or DOM element instance. useImperativeHandle can be used to assign a different value to current.

    As always when forwarding refs, the target component is implemented as a React.forwardRef callback, with props and ref arguments. If it needs to reference one of its own children, it creates its own ref and assigns that as usual; it does not use the ref it received as a parameter, as that will be read by the target’s parent. The component passes the ref it received to useImperativeHandle, along with a callback that accepts no arguments, and returns the value or object that should be assigned to current in the customized ref:

    function PanPowBase(aProps, aRef) {
      const oRefBtn = useRef();
      function ouReady() {
        oRefBtn.current?.focus();
        ...
      }
      // Assign an object containing the 'Ready' function to
      // the parent's ref:
      useImperativeHandle(aRef, () => ({Ready: ouReady}));
    
      return (
        <>
          <div>
            Power
            <button ref={oRefBtn} onClick={...}>Run</button>
          </div>
          ...
        </>
      );
    }
    

    React.forwardRef produces the finished component:

    const PanPow = React.forwardRef(PanPowBase);
    

    The parent assigns a ref to the target as usual, and obtains the target’s data from the ref object’s current property:

    function PanMain(aProps) {
      const oRefPanPow = useRef(null);
      const ouReady = () => oRefPanPow.current?.uReady();
    
      return (
        <div>
          <PanPow ref={oRefPanPow} />
          <button onClick={ouReady}>Ready</button>
        </div>
      );
    }
    

    Memoization hooks

    Memoization is the caching and reuse of one function’s output by another function. Memoization hooks provide an easy way to memoize slow operations within function components.

    useMemo accepts the target function, plus an array of state variables. It does not pass arguments when it invokes the target, so an arrow function may be used to capture and forward props or state variables:

    function PanFilt(aProps) {
      const [oFreq, ouSet_Freq] = useState(800);
      const [oWth, ouSet_Wth] = useState(1);
    
      const oCoeffs = useMemo(
        () => CoeffsFilt(oFreq, oWth),
        [oFreq, oWth]
      );
      ...
    

    Much like useEffect, values in the state array are stored and compared each time the hook is invoked; the target function is evaluated the first time the hook runs, and again if any state value changes. useMemo caches only the most recent target result. The array elements typically match the state variables that were captured by the target function, and eslint-plugin-react-hooks warns if any of these are omitted in the array. If the array is empty, the target will be invoked only once. If the array argument is omitted, the target will be invoked every time, bypassing the memoization entirely.

    Callback memoization

    Sometimes it is desirable to pass a function to a child element through one of its props, for use as an event handler. However, if this handler is defined in a function component, a new function instance will be created during each render. The resulting props change will cause the child to re-render every time the parent renders, even if the child uses PureComponent or React.memo.

    This problem can be avoided with useCallback, a hook that memoizes function instances, rather than function results. Like useMemo, it accepts a target function and an array of state variables. Rather than invoking the target, however, it returns a new function that wraps the target, and it continues to return the same wrapper instance until one or more of the state values change:

    const ouCkEnab = useCallback(
      function (aPos) {
        return !oEntUser || EntWord.uCkTogAt(oEntUser, aPos);
      },
      [ oEntUser ]
    );
    

    Note that the same could easily be done with useMemo, since:

    const ouHand = useCallback(func, vars);
    

    is equivalent to:

    const ouHand = useMemo(() => func, vars);
    

    Custom hooks

    A custom hook is a function that calls other hooks, allowing related state management functionality to be collected and reused in different components. The custom hook’s name must begin with use, but the function itself can have any signature. This allows the hook to gather any sort of data, perform stateful work, and then return any sort of result, for use during the component render.

    The rules that govern built-in hooks also apply to custom hooks. In particular, custom hooks that are called once must be called every time, and in the same order.

    Sources

    Building Your Own Hooks

    Debug string hooks

    When a component is selected in the React DevTools Components tab, its hooks and their associated data are listed in the hooks section within the component properties. Custom hooks are also listed there. If a string is passed to useDebugValue, that value will be displayed in a label next to the custom hook name.