Events
You'll eventually need to detect when components are added, removed, or modified. For example, you may want to know when a component is attached to, or detached from, an entity in order to trigger a change in the ECS or notify a third-party library. You can use some of Javelin's built-in effects and methods to react to these kinds of events.
Monitors#
The best way to detect when an entity matches or no longer matches a query is with a monitor. useMonitor is an effect that accepts a query and executes callbacks when an entity meets or no longer meets the query's criteria.
useMonitor accepts onEnter and onExit callback functions. An entity is only included in a monitor's results once while it continues to match the query. An entity is eligible again only if it is excluded (i.e. due to a change in its archetype) and re-included.
const spooky = createQuery(Enemy, Ghost)
const controlAi = () => {
  useMonitor(
    spooky,
    entity => {}, // entity matches query `spooky`
    entity => {}, // entity no longer matches query `spooky`
  )
}
In the above example, the entity passed to the onEnter callback is an entity who made one of the following type transitions last step:
from    | to
--------|----------------
()      | (Enemy, Ghost)
(Enemy) | (Enemy, Ghost)
(Ghost) | (Enemy, Ghost)
Below is an example of an entity transitioning between multiple archetypes, and whether or not that transition would result in the entity being passed to the onEnter callback:
(Enemy)                  -> excluded
(Enemy, Ghost)           -> included
(Enemy, Ghost, Confused) -> excluded
(Ghost, Confused)        -> excluded
(Enemy, Ghost)           -> included
Component Changes#
The onEnter and onExit callbacks are also provided query results as well as a diff containing the components whose changes triggered the monitor.
useMonitor(
  bodies,
  (entity, [position, velocity], diff) => {
    if (diff[0]) {
      /* position was attached */
    }
  },
  (entity, [position, velocity], diff) => {
    if (diff[0]) {
      /* position was detached */
    }
  },
)