import Layout from '../../lib/components/Layout';
export default Layout;

# Middleware

Middleware are plain objects that modify the positioning
coordinates in some fashion, or provide useful data for the
consumer to use. Most of Floating UI's features are implemented
as a middleware.

`computePosition(){:js}` starts with basic positioning via
`placement{:.objectKey}`:

```js
computePosition(referenceEl, floatingEl, {
  placement: 'right',
  middleware: [],
});
```

Middleware are executed as an in-between "middle" step of the
basic placement computation and eventual return of data to the
caller.

## Example

```js
const shiftByOnePixel = {
  name: 'shiftByOnePixel',
  fn({x, y}) {
    return {
      x: x + 1,
      y: y + 1,
    };
  },
};
```

This simple (but not particularly useful) middleware just adds
`1{:js}` pixel to the coordinates. To use this middleware, add it
to your `middleware{:.objectKey}` array:

```js
computePosition(referenceEl, floatingEl, {
  placement: 'right',
  middleware: [shiftByOnePixel],
});
```

Here, `computePosition(){:js}` will compute coordinates that will
place the floating element to the right of the reference element,
lying flush with it. Middleware are then executed, resulting in
these coordinates getting shifted by one pixel. Then that data is
returned to the caller.

### Shape

A middleware is an object which has a `name{:.objectKey}`
property and a `fn{:.function}` property. The `fn{:.function}`
property provides the logic of the middleware, which returns new
positioning coordinates or useful data.

### Data

Any data can be passed via an optional `data{:.objectKey}`
property of the object that is returned from `fn{:.function}`.
This will be accessible to the consumer via the
`middlewareData{:.param}` property:

```js {7-9}
const shiftByOnePixel = {
  name: 'shiftByOnePixel',
  fn({x, y}) {
    return {
      x: x + 1,
      y: y + 1,
      data: {
        amount: 1,
      },
    };
  },
};
```

```js
computePosition(referenceEl, floatingEl, {
  middleware: [shiftByOnePixel],
}).then(({middlewareData}) => {
  console.log(middlewareData.shiftByOnePixel);
});
```

### Function

You may notice that Floating UI's core middleware are actually
functions. This is so you can pass options in:

```js
const shiftByAmount = (amount = 0) => ({
  name: 'shiftByAmount',
  fn: ({x, y}) => ({
    x: x + amount,
    y: y + amount,
  }),
});
```

It returns an object and uses a closure to pass the configured
behavior:

```js
const middleware = [shiftByAmount(10)];
```

### Always return an object

Inside `fn{:.function}` make sure to return an object. It doesn't
need to contain properties, but to remind you that it should be
pure, you must return an object. Never mutate any values that get
passed in from `fn{:.function}`.

## MiddlewareArguments

An object is passed to `fn{:.function}` containing useful data
about the middleware lifecycle being executed.

In the previous examples, we destructured `x{:.param}` and
`y{:.param}` out of the `fn{:.function}` parameter object. These
are only two properties that get passed into middleware, but
there are many more.

The properties passed are below:

```ts
type MiddlewareArguments = Coords & {
  initialPlacement: Placement;
  placement: Placement;
  strategy: Strategy;
  middlewareData: MiddlewareData;
  elements: Elements;
  rects: ElementRects;
  platform: Platform;
};
```

### x

This is the x-axis coordinate to position the floating element
to.

### y

This is the y-axis coordinate to position the floating element
to.

### elements

This is an object containing the reference and floating elements.

### rects

This is an object containing the `Rect{:.class}`s of the
reference and floating elements, an object of shape
`{width, height, x, y}`.

### middlewareData

This is an object containing all the data of any middleware at
the current step in the lifecycle. The lifecycle loops over the
`middleware{:.objectKey}` array, so later middleware have access
to data from any middleware run prior.

### strategy

The positioning strategy.

### initialPlacement

The initial (or preferred) placement passed in to
`computePosition(){:js}`.

### placement

The stateful resultant placement. Middleware like
`flip{:.function}` change `initialPlacement{:.objectKey}` to a
new one.

### platform

An object containing methods to make Floating UI work on the
current platform, e.g. DOM or React Native.

## Ordering

The order in which middleware are placed in the array matters, as
middleware **use** the coordinates that were returned from
previous ones. This means they perform their work based on the
current positioning state.

Three `shiftByOnePixel{:.const}` in the middleware array means
the coordinates get shifted by 3 pixels in total:

```js
const shiftByOnePixel = {
  name: 'shiftByOnePixel',
  fn: ({x, y}) => ({x: x + 1, y: y + 1}),
};
const middleware = [
  shiftByOnePixel,
  shiftByOnePixel,
  shiftByOnePixel,
];
```

If the later `shiftByOnePixel` implementations had a condition
based on the current value of `x{:.param}` and `y{:.param}`, the
condition can change based on their placement in the array.

Understanding this can help in knowing which order to place
middleware in, as placing a middleware before or after another
can produce a different result.

In general, `offset{:.function}` should always go at the
beginning of the middleware array, while `arrow{:.function}` and
`hide{:.function}` at the end. The other core middleware can be
shifted around depending on the desired behavior.

```js
const middleware = [
  offset(),
  // ...
  arrow({element: arrowElement}),
  hide(),
];
```

## Resetting the lifecycle

There are use cases for needing to reset the middleware lifecycle
so that other middleware perform fresh logic.

- When `flip{:.function}` and `autoPlacement{:.function}` change
  the placement, they reset the lifecycle so that other
  middleware that modify the coordinates based on the current
  `placement{:.objectKey}` do not perform stale logic.
- `size{:.function}` resets the lifecycle with the newly applied
  dimensions, as many middleware read the dimensions to perform
  their logic.
- `inline{:.function}` resets the lifecycle when it changes the
  reference rect to a custom implementation, similar to a
  [Virtual Element](/docs/virtual-elements).

In order to do this, add a `reset{:.objectKey}` property to the
returned object from `fn{:.function}`.

```ts
type Reset =
  | true
  | {
      placement?: Placement;
      // `true` will compute the new `rects` if the
      // dimensions were mutated. Otherwise, you can
      // return your own new rects.
      rects?: true | ElementRects;
    };
```

```js
const middleware = {
  name: 'middleware',
  fn() {
    if (someCondition) {
      return {
        reset: {
          placement: nextPlacement,
        },
      };
    }

    return {};
  },
};
```

> Ensure `someCondition{:js}` will evaluate to `false{:js}` at
> some point, otherwise, this will cause an **infinite loop!**
> When running the library in DEV mode, an error will be thrown
> to prevent an infinite loop from locking up your application.

Data supplied to `middlewareData{:.param}` is **preserved** by
doing this, so you can read it at any point after you've reset
the lifecycle, e.g. if you want to add a `skip{:.objectKey}`
property to break the condition.
