liveBook · Manning

Stateful component

As explained in Build a Frontend Web Framework (From Scratch)

A user-interface component that owns and updates its own state over its lifecycle.

A stateful component owns data that can change while it is in use. Each instance has its own state and lifecycle, and its view changes when that state changes. This avoids keeping every piece of state and every reducer at the application level: a change can re-render the responsible component rather than the whole application. section 9.1 section 9.0

A small implementation separates four responsibilities:

  • Define a component class with a render() method that returns its virtual DOM. A defineComponent() factory accepts the component-specific render function and returns a class that can be instantiated. section 9.2
  • Create state for each instance in its constructor. A state(props) function can use incoming props to choose an initial value; if no state function is supplied, the initial state is an empty object. section 9.3
  • Mount the rendered view into a DOM host and unmount it when finished. Track whether the instance is mounted so mounting twice or unmounting too early raises an error. section 9.2
  • Change state through updateState(), not by directly assigning to state. The method updates the state and patches the component’s view; direct assignment would not tell the component to refresh. section 9.1.1 section 9.4

A counter with its own starting value

A counter can define state(props) to return { count: props.initialCount ?? 0 }. Creating it with { initialCount: 10 } gives that instance a starting count of 10. Another instance can start at the default of 0; each then maintains its own state. section 9.3 section 9.1

Listing 9.3 Adding the state() method (component.js)
export function defineComponent({ render, state }) {  #1
  class Component {
    #isMounted = false
    #vdom = null
    #hostEl = null

    constructor(props = {}) {
      this.props = props  #2
      this.state = state ? state(props) : {}  #3
    }

    render() {
      return render()
    }

    mount(hostEl, index = null) {
 // --snip-- //
    }

    unmount() {
 // --snip-- //
    }
  }

  return Component
}

The constructor shows where incoming props and per-instance state are stored; the render, mount, and unmount methods remain part of the component class. section 9.3

State and props have different jobs: state is maintained internally, while props arrive from a parent. A class is one way to implement this pattern, not a requirement of every framework: React also has stateful functional components using useState(). In a component hierarchy, the application can receive just a root component instead of a global state object and reducers. section 9.1.1 section 9.1 section 12.4

How Build a Frontend Web Framework (From Scratch) covers it

How deep it goes
Builds from a class and factory through instance initialization and mounting, then identifies updateState() as the view-patching path.
Examples
Uses a counter with a props-based initial count and component.js listings for the factory, constructor, and lifecycle methods.
What it assumes
Assumes familiarity with DOM manipulation, virtual DOM reconciliation, stateless components, and centralized reducers; introduces component classes from an empty class.
What you can do after
Build it from scratch with per-instance state, mounting, unmounting, and state updates that patch a component’s view.
How it connects
Connects stateful components to props, virtual DOM reconciliation, lifecycle methods, reducers, and functional components.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Build a Frontend Web Framework (From Scratch). The section links point to the parts of the book it draws on.