A render function turns data into a representation of a user interface. The representation might be actual DOM elements or a virtual DOM tree; a separate step can place it in the browser. Keeping those jobs distinct makes it easier to see what changes when application state changes. section 2.2.3 section 5.2.2
A small to-do application shows three related responsibilities:
- Create a representation: renderTodoInReadMode(todo) makes an <li> with the description and a Done button. renderTodoInEditMode(todo) makes an <li> with an input and Save and Cancel buttons. section 2.2.3
- Place or replace it: initialization appends each rendered to-do; entering edit mode replaces the read-mode element. Adding or updating a to-do likewise uses a render function before changing the displayed list. section 2.2.3
- Respond to interaction: listeners attached while creating an element handle actions such as double-clicking to edit or clicking Done to remove a to-do. section 2.2.3
One to-do, two views
For a to-do whose description is “Water the plants,” read mode returns a list item containing that text and a Done button. Double-clicking its text creates the edit-mode list item and replaces the old one. Cancel creates the read-mode item again; Save updates the description and renders its read-mode replacement. section 2.2.3
In a virtual-DOM application, the top-level view is itself a function of state. A simple renderApp() destroys the previous DOM tree if one exists, calls view(state) to obtain a new virtual DOM tree, and passes that tree to mountDOM(). Initially, mounting the application calls this renderer once; a state manager is needed to request later renders when state changes. section 5.2.2
Listing 5.5 The application instance with its renderer (app.js)
import { destroyDOM } from './destroy-dom'
import { mountDOM } from './mount-dom'
export function createApp({ state, view }) { #1
let parentEl = null
let vdom = null
function renderApp() {
if (vdom) {
destroyDOM(vdom) #2
}
vdom = view(state)
mountDOM(vdom, parentEl) #3
}
return {
mount(_parentEl) { #4
parentEl = _parentEl
renderApp()
},
}
}
The createApp() listing separates producing the new virtual DOM with view(state) from mounting it with mountDOM(). section 5.2.2
For a stateful component, render() returns virtual DOM based on its current props and state. A component class can be created from a supplied render function, so different component types define different views. Mounting calls render() and mounts its result; subsequent state or props updates patch the component’s DOM using a new virtual DOM. When a render function needs the component instance as this, it must be bound to that instance. section 9.1.2 section 9.2 section 9.4
