dojo dragon main logo

Introduction

Dojo's middleware system provides a way to manage asynchronous or imperative APIs in a reactive way, as well as influence behavior and the property API of any composing function-based widgets or other middleware.

Several core and optional middleware are provided by the framework, however, application developers can easily write their own.

Feature Description
Reactive DOM access Via middleware, function-based widgets can work with concrete information and APIs from the sections of the DOM corresponding to their output nodes.
Widget render lifecycle control Middleware can control the portions of Dojo's rendering pipeline for any composing widgets, such as invalidating widgets when they require a rendering update. Widget rendering can also be paused and resumed, allowing for short-circuiting of the render output while waiting for critical information to become available.
A variety of framework-provided middleware Dojo provides optional middleware that allow widgets to implement a range of functionality such as responding to and controlling focus, simple value caching, responding to element intersection and resize events, CSS theming, internationalization and build-time rendering amongst others.
Easy composition and re-use The middleware design is closely aligned with function-based widgets, allowing consistent composition within a widget hierarchy as well as re-use when developing custom middleware.

Basic usage

Creating middleware

  • Defining middleware via the same create() primitive used for defining function-based widgets
  • Optionally defining a properties interface that augments the properties interface of widgets that compose the middleware. Property values are given when creating instances of the composing widgets.
  • Returning a simple function reference that defines the middleware's API that other composing widgets or middleware can interact with

src/middleware/myMiddleware.ts

import { create } from '@dojo/framework/core/vdom';

const factory = create().properties<{ middlewareProp?: boolean }>();

export const myMiddleware = factory(({ properties }) => {
	return () => {
		return properties().middlewareProp ? 'Conditional is true' : 'Conditional is false';
	};
});

export default myMiddleware;

Composing middleware

  • Combining middleware and returning an object to expose a more complex API
  • Using the core invalidator middleware to flag the composing widget as requiring re-rendering

src/middleware/myComposingMiddleware.ts

import { create, invalidator } from '@dojo/framework/core/vdom';
import myMiddleware from './myMiddleware';

const factory = create({ myMiddleware, invalidator });

export const myComposingMiddleware = factory(({ middleware: { myMiddleware, invalidator } }) => {
	return {
		get() {
			return myMiddleware();
		},
		set() {
			invalidator();
		}
	};
});

export default myComposingMiddleware;

Using middleware within a widget

  • Augmenting the widget's properties interface with additional properties from any middleware used
  • Passing in middleware properties when using widgets that compose them

src/widgets/MyWidget.tsx

import { create, tsx } from '@dojo/framework/core/vdom';
import myComposingMiddleware from '../middleware/myComposingMiddleware';

const factory = create({ myComposingMiddleware });

export default factory(function MyWidget({ properties, middleware: { myComposingMiddleware } }) {
	return (
		<virtual>
			<div>{`Middleware property value: ${properties.middlewareProp}`}</div>
			<div>{`Middleware usage: ${myComposingMiddleware.get()}`}</div>
		</virtual>
	);
});

src/main.tsx

import renderer, { tsx } from '@dojo/framework/core/vdom';
import MyWidget from './widgets/MyWidget';

const r = renderer(() => <MyWidget middlewareProp={true} />);
r.mount();