Skip to content

Latest commit

 

History

History
88 lines (71 loc) · 2.44 KB

messaging-pattern.md

File metadata and controls

88 lines (71 loc) · 2.44 KB

Messaging pattern

The messaging pattern allows components to communicate with each other, but unlike the mediator pattern which is local communication mechanism, this one is semi-global. The internals are loosely fastened to the component tree. In modern front-end applications message passing is usually implemented via a global state container and is not properly encapsulated - which is the main goal of patterns in general.

In the messaging pattern components exchange messages which is a common technique to programming in general. The messaging pattern abstracts the way in which components are embedded within the component tree and allow them to communicate via a queue.

Let us reconsider the tooltip example used in the mediator pattern and apply the messaging pattern instead:

const Trigger = ({ tooltipId }) => {
  const message = {
    action: 'toggle',
    tooltipId,
  }

  return (
    <Icon
      type="question-mark"
      onClick={() => window.postMessage(message)}
    />
  )
}

The Trigger component sends a message when clicked. Why do we use the window messaging API ? For simplicity, because it is the messaging mechanism implemented in native JavaScript. The Tooltip component listens to arriving messages and updates the state when necessary.

const Tooltip = ({ tooltipId }) => {
  const [ open, setOpen ] = useState(false)

  useEffect(() => {
    function receiveMessage(event) {
      /* TODO: check origin for security */

      if (event.data.tooltipId !== tooltipId) {
        return;
      }

      if (event.data.action === 'toggle') {
        setOpen(open => !open)
      }
    }

    window.addEventLister('message', receiveMessage)

    return () => {
      window.removeEventListener('message', receiveMessage)
    }
  }, [])

  return (
    <Balloon
      visible={open}
      onClick={() => setOpen(false)}
    />
  )
}

The messages are identified by a string, we need to make sure that those identifiers are unique. Here is how the invocation of the components looks like:

<React.Fragment>
  <Trigger tooltipId="example-tooltip" />
  <Tooltip tooltipId="example-tooltip" />
</React.Fragment>

Although this approach is ready to go, it has also some drawbacks - you cannot pass functions and promises, but should be able to so. However it is not difficult to implement the messaging internals ourselves to circumvent this limitation, bypass eventual security problems, and make this mechanism semi-global.