{"action":"created","issue":{"url":"https://api.github.com/repos/hyperapp/hyperapp/issues/178","repository_url":"https://api.github.com/repos/hyperapp/hyperapp","labels_url":"https://api.github.com/repos/hyperapp/hyperapp/issues/178/labels{/name}","comments_url":"https://api.github.com/repos/hyperapp/hyperapp/issues/178/comments","events_url":"https://api.github.com/repos/hyperapp/hyperapp/issues/178/events","html_url":"https://github.com/hyperapp/hyperapp/pull/178","id":219354635,"number":178,"title":"Implement event/hook emitter","user":{"login":"pedroborges","id":688309,"avatar_url":"https://avatars2.githubusercontent.com/u/688309?v=3","gravatar_id":"","url":"https://api.github.com/users/pedroborges","html_url":"https://github.com/pedroborges","followers_url":"https://api.github.com/users/pedroborges/followers","following_url":"https://api.github.com/users/pedroborges/following{/other_user}","gists_url":"https://api.github.com/users/pedroborges/gists{/gist_id}","starred_url":"https://api.github.com/users/pedroborges/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pedroborges/subscriptions","organizations_url":"https://api.github.com/users/pedroborges/orgs","repos_url":"https://api.github.com/users/pedroborges/repos","events_url":"https://api.github.com/users/pedroborges/events{/privacy}","received_events_url":"https://api.github.com/users/pedroborges/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":6,"created_at":"2017-04-04T19:18:04Z","updated_at":"2017-04-15T13:50:13Z","closed_at":"2017-04-15T13:50:13Z","pull_request":{"url":"https://api.github.com/repos/hyperapp/hyperapp/pulls/178","html_url":"https://github.com/hyperapp/hyperapp/pull/178","diff_url":"https://github.com/hyperapp/hyperapp/pull/178.diff","patch_url":"https://github.com/hyperapp/hyperapp/pull/178.patch"},"body":"This PR relates to #177, #174, #150, #144.\r\n\r\nAfter an interesting discussing on Slack, I've modified the emitter implementation. Below is a detailed explanation of what have changed since my [last comment](https://github.com/hyperapp/hyperapp/issues/174#issuecomment-291198564) on issue #174.\r\n\r\nFrom the Slack discussion it was clear that `subscriptions` is the best name to group both `events` and `hooks` since @jbucaran wants to avoid having both. The following is a list of all events and hooks emitted by the core of Hyperapp:\r\n\r\n```js\r\nsubscriptions: {\r\n    '*': (type) => {},                   // Listens to all events/hooks\r\n    'actions': (name, data) => {},       // When any action is called\r\n    'action:save': (data) => {},         // When a specific action is called\r\n    'update': (name) => {},              // When model updates\r\n    'load': (model, actions) => {        // When the core loads right before the first render\r\n        if (model.contacts.length < 1) {\r\n            actions.contacts.addRandom()\r\n        }\r\n    },\r\n    'render': (model, view) => {         // When rendering\r\n        // do something and\r\n        // return a modified view\r\n        return view\r\n    }\r\n}\r\n```\r\n\r\nI have integrated [Mitt](https://github.com/developit/mitt) in Hyperapp storing all handlers on the `subscription` object. Mitt methods `on` and `off` have been removed. The only way to register event/hook handlers now is via the `subscriptions` object when initialising the app/plugin.\r\n\r\nAlso, Mitt is no longer exposed to the app. Instead only actions will receive an `emit` object allowing them to trigger an `event` and/or `hook`:\r\n\r\n- **`emit.event` -** is a way to notify that something happened **without** expecting any value to be returned.\r\n\r\n```js\r\nactions: {\r\n  router: {\r\n    match: match,\r\n    go: function (_, data, actions, emit) {\r\n      history.pushState({}, \"\", data)\r\n      actions.router.match(data)\r\n      emit.event('route:update', data)\r\n    }\r\n  }\r\n}\r\n```\r\n\r\n- **`emit.hook` -** **expects** a value to be returned allowing the app/plugin to act based on it. Under the hood this method uses `Array.reduce` to store the value returned since more than one handler may exist for a given hook. The value that will be returned from the hook is stored on the **last argument** allowing the app/plugin to change or replace this value.\r\n\r\n```js\r\nfunction render(model, view) {\r\n  view = emit.hook(\"render\", model, view)\r\n\r\n  element = patch(root, element, node, node = view(model, actions))\r\n}\r\n```\r\n\r\nIn this example from the core, any handler that subscribes to the `render` hook will be able to change or completely replace the `view`, which will be returned since it's the last argument. The router subscribes to the `render` hook:\r\n\r\n```js\r\nsubscriptions: {\r\n  \"render\": function (model, view) {\r\n    return view[model.router.match]\r\n  }\r\n}\r\n```\r\n\r\n`onLoad` is now `load` and must defined inside `subscribers`. It also no longer receives the `emitter` as I first proposed.\r\n\r\n`onRender` is now `render` for short.\r\n\r\nThese changes maintain the flexibility brought by an event emitter while limiting its use to avoid undesired side effects.\r\n\r\nPrefixing core events/hooks and encouraging plugin authors to do the same is a good idea, keep that mind."},"comment":{"url":"https://api.github.com/repos/hyperapp/hyperapp/issues/comments/294294541","html_url":"https://github.com/hyperapp/hyperapp/pull/178#issuecomment-294294541","issue_url":"https://api.github.com/repos/hyperapp/hyperapp/issues/178","id":294294541,"user":{"login":"jbucaran","id":56996,"avatar_url":"https://avatars2.githubusercontent.com/u/56996?v=3","gravatar_id":"","url":"https://api.github.com/users/jbucaran","html_url":"https://github.com/jbucaran","followers_url":"https://api.github.com/users/jbucaran/followers","following_url":"https://api.github.com/users/jbucaran/following{/other_user}","gists_url":"https://api.github.com/users/jbucaran/gists{/gist_id}","starred_url":"https://api.github.com/users/jbucaran/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jbucaran/subscriptions","organizations_url":"https://api.github.com/users/jbucaran/orgs","repos_url":"https://api.github.com/users/jbucaran/repos","events_url":"https://api.github.com/users/jbucaran/events{/privacy}","received_events_url":"https://api.github.com/users/jbucaran/received_events","type":"User","site_admin":false},"created_at":"2017-04-15T13:50:13Z","updated_at":"2017-04-15T13:50:13Z","body":"@pedroborges Thanks for all the good work man! The emitter made it to the finals and won after all."}}
{"actor":{"display_login":"jbucaran"}}