Workflow

interface Workflow<in PropsT, out OutputT, out RenderingT>(source)

A composable, optionally-stateful object that can handle events, delegate to children, subscribe to arbitrary asynchronous events from the outside world.

The basic purpose of a Workflow is to take some input (in the form of PropsT) and return a rendering. To that end, a workflow may keep track of internal state, recursively ask other workflows to render themselves, subscribe to data streams from the outside world, and handle events both from its renderings and from workflows it's delegated to (its "children"). A Workflow may also emit output events up to its parent Workflow.

Workflows form a tree, where each workflow can have zero or more child workflows. Child workflows are started as necessary whenever another workflow asks for them, and are cleaned up automatically when they're no longer needed. Props propagates down the tree, outputs and renderings propagate up the tree.

Implementing Workflow

The Workflow interface is useful as a facade for your API. You can publish an interface that extends Workflow, and keep the implementation (e.g. is your workflow stateful or stateless a private implementation detail.

You should almost never implement Workflow directly, however. There are two abstract classes that you should subclass instead: StatefulWorkflow and StatelessWorkflow. The differences between them are described below, but both type have a render method that you implement to generate renderings from your props and interact with the runtime (e.g. by changing state or emitting outputs).

Stateful Workflows

If your workflow needs to keep track of internal state, subclass StatefulWorkflow. It has an additional type parameter, StateT, requires you to specify how to create the initial state and how to snapshot/restore your state, and passes the current state to the StatefulWorkflow.render method.

Stateless Workflows

If your workflow does not have any state of its own and simply needs to delegate to other workflows (e.g. transforming props, outputs, or renderings), subclass StatelessWorkflow and implement its sole StatelessWorkflow.render method, or just pass a lambda to the stateless function.

Interacting with events and other workflows

All workflows are passed a RenderContext in their render methods. This context allows the workflow to interact with the outside world by doing things like listening for events, subscribing to streams of data, rendering child workflows, and performing cleanup when the workflow is about to be torn down by its parent. See the documentation on BaseRenderContext for more information about what it can do.

Things to avoid

Mutable instance state

Classes that implement Workflow should not contain mutable properties. Such properties are instance-specific state and can introduce buggy behavior. Instead, subclass StatefulWorkflow and move all your state to the workflow's StateT type. For example, setting a property will not cause your workflow to be re-rendered – the runtime has no way of knowing that some data it doesn't know about has changed. It can also break consumers of your workflows, who may be expecting to be able to re-use the same instance of your workflow type in different places in the workflow tree – this works if all the workflow's state is contained in its StateT, but not if the instance has its own properties. (Note that storing dependencies, which are effectively static relative to the workflow instance, in properties is fine.)

Render side effects

Workflows' render methods must not perform side effects or read mutable state. They can contain logic, but the logic must be based on the PropsT, the StateT if present, and the renderings from other workflows. They must declare what work to perform (via Workers), and what data to render (via RenderingTs and child workflows). For this reason, programming with workflows can be considered declarative-style programming.

Parameters

PropsT

Typically a data class that is used to pass configuration information or bits of state that the workflow can always get from its parent and needn't duplicate in its own state. May be Unit if the workflow does not need any props data.

OutputT

Typically a sealed class that represents "events" that this workflow can send to its parent. May be Nothing if the workflow doesn't need to emit anything.

RenderingT

The value returned to this workflow's parent during composition. Typically represents a "view" of this workflow's props, current state, and children's renderings. A workflow that represents a UI component may use a view model as its rendering type.

See also

Inheritors

Types

Link copied to clipboard
object Companion

Empty companion serves as a hook point to allow us to create Workflow.foo extension methods elsewhere.

Properties

Link copied to clipboard

The computed WorkflowIdentifier for this Workflow. Any IdCacheable Workflow should call this and then store the value in the cachedIdentifier property so as to prevent the extra work needed to create the WorkflowIdentifier and look up the class name each time.

Link copied to clipboard

The WorkflowIdentifier that identifies this Workflow.

Functions

Link copied to clipboard

Provides a StatefulWorkflow view of this workflow. Necessary because StatefulWorkflow is the common API required for BaseRenderContext.renderChild to do its work.

Link copied to clipboard