diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 32906c913..000000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,91 +0,0 @@
-# Contributing to HyperApp
-
-HyperApp is an open source project and we love to receive contributions from our community. You can start to contribute in many ways, from writing tutorials or blog posts, improving the documentation, filing bug reports and requesting new features.
-
-## Quick Start
-
-Clone the project and install the dependencies.
-
-```sh
-git clone https://github.com/hyperapp/hyperapp
-cd hyperapp
-npm i
-```
-
-Run the tests.
-
-```
-npm run test
-```
-
-## Filing Bugs
-
-- Before submitting a bug report, search the issues for similar tickets. Your issue may have already been discussed or resolved. Feel free to add a comment to an existing ticket, even if it's closed.
-
-- Determine which repository the problem should be reported in. If you have an issue with the website, you'll be better served in [hyperapp/website](https://github.com/hyperapp/website), etc.
-
-- If you would like to share something cool you've made with HyperApp, check out [hyperapp/awesome](https://github.com/hyperapp/awesome-hyperapp).
-
-- If you have a question or need help with something you are building, we recommend joining the [HyperApp Slack Team](https://hyperappjs.herokuapp.com).
-
-- Be thorough in your title and report, don't leave out important details, describe your setup and include any relevant code with your issue.
-
-- Use GitHub [fenced code blocks](https://help.github.com/articles/creating-and-highlighting-code-blocks/) to share your code. If your code has JSX in it, please use ```jsx for accurate syntax highlighting.
-
-## Code Style
-
-- Prefer descriptive single-word variable / function names to single-letter names.
-
-- Consider improving the [Implementation Notes](/docs/implementation-notes.md) section in the documentation before adding comments to the code.
-
-- Format your code before adding a commit using [prettier](https://prettier.github.io/prettier) or running the format script.
-
- ```
- npm run format
- ```
-
-- With the exception of the ES6 module syntax, HyperApp is written in ES5.
-
-- We prefer keeping all the moving parts inside as few files as possible. While this may change in the future, we don't intend to break the library into smaller modules.
-
-## Core Values
-
-- HyperApp was born out of the attempt to do more with less.
-
-- HyperApp's design is based on the Elm Architecture and application development is similar to React/Redux using a single immutable state tree.
-
-- The ideal bundle size is 1 KB, but no more than 1.5 KB.
-
-## Writing Tests
-
-- We use [Babel](https://babeljs.io) and [Jest](http://facebook.github.io/jest) to run the tests.
-
-- Feel free to create a new test/*.test.js file if none of the existing test files suits your test case.
-
-- Tests usually create an application with [app](/docs/api.md#app) and check if the document.body.innerHTML matches some expected string. The app() call is async, so we sometimes use the [loaded](/docs/api.md#loaded) event to detect when the view has been attached to the document.
-
-- HyperApp uses [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) under the hood, but it is not natively supported by Jest. For this reason you'll often see the following code at the top of a test file:
-
- ```js
- window.requestAnimationFrame = setTimeout
- ```
-
-## Code of Conduct
-
-In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
-
-Examples of behavior that contributes to creating a positive environment include:
-
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-
-Examples of unacceptable behavior by participants include:
-
-* The use of sexualized language or imagery and unwelcome sexual attention or advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at . The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
-
-This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org).
diff --git a/README.md b/README.md
index ffd90487b..61aa3e55a 100644
--- a/README.md
+++ b/README.md
@@ -6,46 +6,44 @@
HyperApp is a JavaScript library for building frontend applications.
-[Elm Architecture]: https://guide.elm-lang.org/architecture/
-[Hyperx]: https://github.com/substack/hyperx
-[JSX]: https://facebook.github.io/react/docs/introducing-jsx.html
-[CDN]: https://unpkg.com/hyperapp
-
-* **Minimal**: HyperApp was born out of the attempt to do more with less. We have aggressively minimized the concepts you need to understand while remaining on par with what other frameworks can do.
-* **Functional**: HyperApp's design is based on the [Elm Architecture]. Create scalable browser-based applications using a functional paradigm. The twist is you don't have to learn a new language.
-* **Batteries-included**: Out of the box, HyperApp combines state management with a Virtual DOM engine that supports keyed updates & lifecycle events — all with no dependencies.
+- **Minimal**: HyperApp was born out of the attempt to do [more with less](https://en.wikipedia.org/wiki/Worse_is_better). We have aggressively minimized the concepts you need to understand while remaining on par with what other frameworks can do.
+- **Functional**: HyperApp's design is based on [The Elm Architecture](https://guide.elm-lang.org/architecture). Create scalable browser-based applications using a functional paradigm. The twist is you don't have to learn a new language.
+- **Batteries-included**: Out of the box, HyperApp combines state management with a Virtual DOM engine that supports keyed updates & lifecycle events — all with no dependencies.
[Get started with HyperApp](/docs/getting-started.md)
## Hello World
-[Try it online](https://codepen.io/hyperapp/pen/zNxZLP?editors=0010)
+[Try it Online](https://codepen.io/hyperapp/pen/zNxZLP?editors=0010)
```jsx
app({
- state: 0,
+ state: {
+ count: 0
+ },
view: (state, actions) =>
-
{state}
-
-
+
{state.count}
+
+
,
actions: {
- add: state => state + 1,
- sub: state => state - 1
+ sub: state => ({ count: state.count - 1 }),
+ add: state => ({ count: state.count + 1 })
}
})
```
## Documentation
-The documentation is located in the [/docs](/docs) directory.
+The documentation is in the [docs](/docs) directory.
## Community
-* [Slack](https://hyperappjs.herokuapp.com)
-* [/r/hyperapp](https://www.reddit.com/r/hyperapp)
-* [Twitter](https://twitter.com/hyperappjs)
+- [Slack](https://hyperappjs.herokuapp.com)
+- [/r/hyperapp](https://www.reddit.com/r/hyperapp)
+- [CodePen](https://codepen.io/hyperapp)
+- [Twitter](https://twitter.com/hyperappjs)
## License
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
new file mode 100644
index 000000000..b62dbe467
--- /dev/null
+++ b/docs/CONTRIBUTING.md
@@ -0,0 +1,44 @@
+# Contributing to HyperApp
+
+Thank you for taking the time to read our contribution guidelines. You can start to contribute in many ways, from writing tutorials, improving the documentation, filing bug reports and requesting new features.
+
+## Code of Conduct
+
+Our open source community strives to:
+
+- **Be nice.**
+- **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities.
+- **Be considerate**: Remember that we're a world-wide community, so you might not be communicating in someone else's primary language.
+- **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners.
+- **Be careful in the words that you choose**: We are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable.
+
+This code is not exhaustive or complete. It serves to distill our common understanding of a collaborative, shared environment, and goals. We expect it to be followed in spirit as much as in the letter.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting us at .
+
+## Code Style
+
+- Prefer descriptive single-word variable / function names to single-letter names.
+- Format your code before committing using [prettier](https://prettier.github.io/prettier) or run the `format` script.
+- We use ES6 modules but the rest of the code base is written in ES5.
+- We prefer keeping all the moving parts inside as few files as possible. We don't have plans to break up the library into smaller modules.
+
+## Filing Bugs
+
+- Before submitting a bug report, search the issues for similar tickets. Your issue may have already been discussed and resolved. Feel free to add a comment to an existing ticket, even if it's closed.
+- Determine which repository the problem should be reported in. If you have an issue with the Router, you'll be better served in [hyperapp/router](https://github.com/hyperapp/router), etc.
+- If you have a question or need help with something you are building, we recommend joining the [HyperApp Slack Team](https://hyperappjs.herokuapp.com).
+- Be thorough in your title and report, don't leave out important details, describe your setup and include any relevant code with your issue.
+- Use GitHub [fenced code blocks](https://help.github.com/articles/creating-and-highlighting-code-blocks/) when sharing code. If your code has JSX in it, please use ```jsx for accurate syntax highlighting.
+
+## Writing Tests
+
+- We use [Babel](https://babeljs.io) and [Jest](http://facebook.github.io/jest) to run the tests.
+- Feel free to create a new `test/*.test.js` file if none of the existing test files suits your test case.
+- Tests usually start by creating a small application and using a feature, then check if `document.body.innerHTML` matches some expected string. The app call is async, so we often use [oncreate](/docs/api.md#oncreate) or [onupdate](/docs/api.md#onupdate) lifecycle events to detect when the view has been rendered.
+- We use [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) to throttle renders, but it is not natively supported by Jest. For this reason you'll often see the following code at the top of a test file:
+
+ ```js
+ window.requestAnimationFrame = setTimeout
+ ```
+
diff --git a/docs/README.md b/docs/README.md
index 8de6d85ba..27f778cfc 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,8 +1,7 @@
# Documentation
-We assume you have some knowledge of HTML and JavaScript. If you are completely new to frontend development, you may prefer to come back after learning the basics. Previous experience with other frameworks is a plus, but not required.
-
- [Index](/docs/index.md)
+- [Contribution Guidelines](/docs/CONTRIBUTING.md)
- [Getting Started](/docs/getting-started.md)
- [Installation](/docs/getting-started.md#installation)
- [Usage](/docs/getting-started.md#usage)
@@ -21,6 +20,7 @@ We assume you have some knowledge of HTML and JavaScript. If you are completely
- [Keys](/docs/keys.md)
- [Components](/docs/components.md)
- [Lifecycle Events](/docs/lifecycle-events.md)
+ - [SSR & Hydration](/docs/hydration.md)
- Learning
- [Implementation Notes](/docs/implementation-notes.md)
- [Tutorials](/docs/tutorials.md)
diff --git a/docs/actions.md b/docs/actions.md
index ce6e57ca5..030545466 100644
--- a/docs/actions.md
+++ b/docs/actions.md
@@ -1,182 +1,132 @@
# Actions
-Use [actions](/docs/api.md#actions) to update the state.
-```jsx
-app({
- state: "Hi.",
- view: (state, actions) =>
-
- {state}
-
,
- actions: {
- ucase: state => state.toUpperCase()
- }
-})
-```
+Actions are functions which take the current [state](/docs/state.md) and return a partial state or a [thunk](#thunks). Actioons are the only way to update the state tree.
-An action must return a partial state or a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolves to a partial state. See [Side Effects](#side-effects).
+[Try it Online](https://codepen.io/hyperapp/pen/qRMEGX?editors=0010)
```jsx
app({
state: {
- count: 0,
- maxCount: 10
+ text: "Hello!",
+ defaultText: "Nice weather today."
},
- view: (state, actions) =>
-
+ view: (state, { setText }) =>
+
-
- ,
- actions: {
- up: ({ count }, actions, data = 0) => ({
- count: count + data
- })
- }
-})
-```
+Actions are often called as a result of user events triggered from the [view](/docs/view.md) or from inside application [events](/docs/events.md).
-## Side Effects
+## Thunks
-Actions are not required to have a return value. You can use them to call other actions, for example after an async operation has completed.
+Actions can return a function instead of a partial state. This function is called a _thunk_. They operate like regular actions but will not trigger a state update unless [`update`](/docs/api.md#update) is called from within the thunk function.
```jsx
app({
- state: {
- count: 0
- },
- view: (state, actions) =>
-
-
- {state.count}
-
-
- ,
actions: {
- up: ({ count }) => ({
- count: count + 1
- }),
- upLater: (state, actions) => {
- setTimeout(actions.up, 1000)
+ defer(state, actions, data) {
+ return update => {
+ // ...
+ update(newData)
+ }
}
}
})
```
-Actions can return a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
+The action returns the result of the thunk, allowing you to modify how actions operate and what types they can return.
+
+Use thunks to defer state updates, create [getters](#getters), scoped mixins, etc.
+
+## Async Updates
+
+Use [thunks](#thunks) to update the state asynchronously, e.g., after a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) resolves.
+
+[Try it Online](https://codepen.io/hyperapp/pen/ZeByKv?editors=0010)
```jsx
app({
- state: 0,
- view: (state, actions) =>
-
-
- {state}
-
-
- ,
actions: {
- upLater: (state, actions) =>
- new Promise(resolve => setTimeout(resolve, 1000, state + 1))
+ getURL(state) {
+ return update => fetch(`/search?q=${state.query}`)
+ .then(data => data.json())
+ .then(json => update({
+ url: json[0].url
+ })
+ )
+ }
}
})
```
-Actions can be written as [async functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) too.
+Actions need not have a return value at all. This way they will not trigger a state update. You can use them to create side effects, call other actions, etc.
```jsx
-const delay = result =>
- new Promise(resolve => setTimeout(resolve, 1000, result))
-
app({
- state: 0,
- view: (state, actions) =>
-
-
- {state}
-
-
- ,
actions: {
- upLater: async state => await delay(state + 1)
+ setURL(state, actions, data) {
+ return { url: data[0].url }
+ },
+ getURL(state, actions) {
+ const req = new XMLHttpRequest()
+
+ req.open("GET", `/search?q=${state.query}`)
+ req.onreadystatechange = () => {
+ if (
+ req.readyState === XMLHttpRequest.DONE &&
+ req.status === 200
+ ) {
+ actions.setURL(JSON.parse(req.responseText))
+ }
+ }
+ req.send()
+ }
}
})
```
-## Namespaces
+## Getters
-Namespaces let you organize actions into categories or domains.
+A getter is an action that retrieves a property from the state tree or the result of a computation.
```jsx
app({
- state: 0,
- view: (state, actions) =>
-
-
-
- {state}
-
-
- ,
actions: {
- counter: {
- up: state => state + 1,
- down: state => state - 1
+ isAdult({ userId }) {
+ return () => state.users[userId].age >= state.adultAge
}
}
})
```
-## Complex State
-
-Suppose we have a complex state object and wish to update a given property avoiding mutation.
-
-Here is one way we could achieve this using [Ramda](https://github.com/ramda/ramda).
+## Namespaces
-[Try it online](https://codepen.io/hyperapp/pen/Zygvbg?editors=0010)
+We iterate over action keys recursively during setup, allowing for nested actions.
```jsx
app({
- state: {
- counters: [{ value: 1 }, { value: 2 }, { value: 4 }]
- },
actions: {
- oneUp: (state, actions, index) => {
- return R.over(
- R.lensPath(["counters", index, "value"]),
- value => value + 1,
- state
- )
- }
+ game: gameActions,
+ score: scoreActions,
+ ...userActions
}
})
```
-See also [lodash/fp](https://github.com/lodash/lodash/wiki/FP-Guide) and [Immutable.js](https://github.com/facebook/immutable-js/) for alternatives.
-
-
-
diff --git a/docs/api.md b/docs/api.md
index 6cac6ff89..312b9c21e 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -1,21 +1,26 @@
-# API
+# Reference
- [h](#h)
- - [VirtualNode](#virtualnode)
- [Component](#component)
+ - [VirtualNode](#virtualnode)
+ - [Attributes](#attributes)
+ - [LifecyleEvents](#lifecyleevents)
- [app](#app)
- [State](#state)
- [View](#view)
- [Actions](#actions)
+ - [ActionInfo](#actioninfo)
- [ActionResult](#actionresult)
+ - [Thunk](#thunk)
- [Events](#events)
- [Default Events](#default-events)
- - [ActionData](#actiondata)
- [CustomEvent](#customevent)
- [Mixins](#mixins)
- [Mixin](#mixin)
-- [emit](#emit)
+ - [Root](#root)
+- [Emit](#emit)
+- [Update](#update)
@@ -24,32 +29,48 @@
-string | number | boolean | object
+{
+ [key: string]:
+ | PartialState
+ | any
+}
### View
-See also [View](/docs/view.md).
+See [View](/docs/view.md).
(State, Actions): VirtualNode
@@ -83,7 +108,7 @@ See also [View](/docs/view.md).
### Actions
-See also [Actions](/docs/actions.md).
+See [Actions](/docs/actions.md).
{
@@ -93,37 +118,47 @@ See also [Actions](/docs/actions.md).
}
+#### ActionInfo
+
+
+{
+ name: string,
+ data: any
+}
+
+
#### ActionResult
-A partial state or [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolves to a partial state.
+A partial [State](#state) or [Thunk](#thunk).
+
+#### Thunk
+
+See [Thunks](/docs/actions.md#thunks).
+
+
diff --git a/docs/components.md b/docs/components.md
index d3ea6ad2b..85ef5fb33 100644
--- a/docs/components.md
+++ b/docs/components.md
@@ -2,7 +2,7 @@
A [component](/docs/api.md#component) is a function that returns a custom [virtual node](/docs/virtual-nodes.md). Components are reusable blocks of code that encapsulate markup, styles and behaviours that belong together.
-[Try it online](https://codepen.io/hyperapp/pen/WRWbKw?editors=0010)
+[Try it Online](https://codepen.io/hyperapp/pen/WRWbKw?editors=0010)
```js
const Title = ({ url, value }/*, children*/) =>
@@ -13,7 +13,7 @@ const Title = ({ url, value }/*, children*/) =>
app({
view: () =>
-
+
})
```
@@ -27,14 +27,14 @@ Here is the corresponding virtual node.
id: "app"
},
children: [{
- tag: "a",
- data: {
- href: "#"
- },
+ tag: "h1",
+ data: {},
children: [{
- tag: "h1",
- data: undefined,
- children: ["Hello."]
+ tag: "a",
+ data: {
+ href: "#"
+ },
+ children: ["Jump"]
}]
}]
}
@@ -51,5 +51,5 @@ const Link = (props, children) =>
## Component Lifecycle Events
-Components share the same lifecycle events available to virtual nodes. See [Lifecyle Events](/docs/lifecycle-events.md) for details.
+Components share the same lifecycle events as virtual nodes. See [Lifecyle Events](/docs/lifecycle-events.md) for more information.
diff --git a/docs/countdown-timer.md b/docs/countdown-timer.md
index ede95f209..2d084745a 100644
--- a/docs/countdown-timer.md
+++ b/docs/countdown-timer.md
@@ -2,7 +2,7 @@
In this example and learn how to use the [events](/docs/events.md) property to register global events.
-[Try it online](https://codepen.io/hyperapp/pen/evOZLv?editors=0010)
+[Try it Online](https://codepen.io/hyperapp/pen/evOZLv?editors=0010)
```jsx
const pad = n => (n < 10 ? "0" + n : n)
@@ -47,7 +47,9 @@ app({
}
},
events: {
- init: (state, actions) => setInterval(actions.tick, 1000)
+ load(state, actions) {
+ setInterval(actions.tick, 1000)
+ }
}
})
```
@@ -75,7 +77,9 @@ To simulate the clock we use [`setInterval`](https://developer.mozilla.org/en-US
```jsx
events: {
- init: (state, actions) => setInterval(actions.tick, 1000)
+ load(state, actions){
+ setInterval(actions.tick, 1000)
+ }
}
```
@@ -91,7 +95,3 @@ if (state.count === 0) {
actions.drop()
}
```
-
-
-
-[Back to Tutorials](/docs/tutorials.md)
diff --git a/docs/counter.md b/docs/counter.md
index e1a198a9c..688c489e5 100644
--- a/docs/counter.md
+++ b/docs/counter.md
@@ -2,60 +2,60 @@
In this example we'll learn how to use [actions](/docs/actions.md) to update the state of your application.
-[Try it online](https://codepen.io/hyperapp/pen/zNxZLP?editors=0010)
+[Try it Online](https://codepen.io/hyperapp/pen/zNxZLP?editors=0010)
```jsx
app({
- state: 0,
+ state: {
+ count: 0
+ },
view: (state, actions) =>
- {state}
+ {state.count}
+
-
,
actions: {
- add: state => state + 1,
- sub: state => state - 1
+ sub: state => ({ count: state.count - 1 }),
+ add: state => ({ count: state.count + 1 })
}
})
```
-The state is a number and its initial value is 0.
+The state consists of a single property: `count` which is initialized to 0.
```jsx
-state: 0
+state: {
+ count: 0
+}
```
The view function receives the state as the first argument and uses it to display the current value of the counter inside an `
` tag.
```jsx
-
{state}
+
{state.count}
```
The view also defines two buttons with `onclick` handlers attached to them. The handlers are available in the actions object that is passed to the view as the second argument.
```jsx
+
-
```
The `disabled` attribute is dynamically toggled depending on the value of the counter. This prevents the decrement button from being clicked when the counter reaches zero.
```jsx
-disabled={state <= 0}
+disabled={state.count <= 0}
```
-Note that neither of the actions update the state directly, instead, they return a new state.
+Note that neither of the actions update the state directly, instead, they return a partial state.
```jsx
-add: state => state + 1,
-sub: state => state - 1
+sub: state => ({ count: state.count - 1 }),
+add: state => ({ count: state.count + 1 })
```
When the state is updated as a result of calling an action, the view function is called and the application is rendered again.
-
-
-
-[Back to Tutorials](/docs/tutorials.md)
diff --git a/docs/events.md b/docs/events.md
index 4798e9a9a..29122cc33 100644
--- a/docs/events.md
+++ b/docs/events.md
@@ -1,8 +1,16 @@
# Events
-Use events to get notified when your app is initialized, an action is called, before a [view](/docs/view.md) is rendered, etc.
+Events are function called at various points in the life of your application.
-[Try it online](https://codepen.io/hyperapp/pen/Bpyraw?editors=0010)
+Use events to get notified before the [state](/docs/state.md) is updated, your [view](/docs/view.md) is rendered, an [action](/docs/actions.md) is called, etc.
+
+## Default Events
+
+### load
+
+The [`load`](/docs/api.md#load) event is fired before the first render. Use it to initialize your application, listen to global events, create a network request, etc.
+
+[Try it Online](https://codepen.io/hyperapp/pen/Bpyraw?editors=0010)
```jsx
app({
@@ -12,51 +20,91 @@ app({
move: (state, actions, { x, y }) => ({ x, y })
},
events: {
- init: (state, actions) =>
+ load(state, actions) {
addEventListener("mousemove", e =>
actions.move({
x: e.clientX,
y: e.clientY
})
)
+ }
}
})
```
-## Default Events
-
-### init
+### action
-The init event fires before the first render. This is a good place to initialize your application, create a network request, access the local [Storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage), etc.
+The [`action`](/docs/api.md#action) event is fired before an action is called. Use it to log action activity, extract information about actions, etc.
-### loaded
+```jsx
+app({
+ events: {
+ action(state, actions, { name, data }) {
+ console.group("Action Info")
+ console.log("Name:", name)
+ console.log("Data:", data)
+ console.groupEnd()
+ }
+ }
+})
+```
-The loaded event fires after the first render. This event is useful if you need to access actual DOM nodes after initialization.
+### resolve
-### beforeAction
+The [`resolve`](/docs/api.md#resolve) event is fired after an action is called, allowing you to intercept its return value. Use it to customize the types actions can return.
-The beforeAction event fires before an action is called. This event can be useful to implement middleware, developer tools, etc.
+Allow actions to return a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
-### afterAction
+```jsx
+app({
+ events: {
+ resolve(state, actions, result) {
+ if (result && typeof result.then === "function") {
+ return result.then(update) && result
+ }
+ }
+ }
+})
+ ```
+Allow actions to return an [Observable](https://github.com/tc39/proposal-observable).
-The afterAction event fires after an action is called. This event can be useful to implement middleware, developer tools, etc.
+```jsx
+app({
+ events: {
+ resolve(state, actions, result) {
+ if (result && typeof result.subscribe == "function") {
+ return update => result.subscribe({ next: update })
+ }
+ }
+ }
+})
+```
### update
-The update event fires before the state is updated. This event can be useful to validate the state before an update takes place.
+The [`update`](/docs/api.md#eventsupdate) event is fired before the state is updated. Use this event to log state changes, validate the new state before an update takes place, etc.
+
+```jsx
+app({
+ events: {
+ update(state, actions, nextState) {
+ if (validate(nextState)) {
+ return nextState
+ }
+ }
+ }
+})
+```
### render
-The render event fires every time before the view is rendered. You can use this event to overwrite the current view by returning a new one.
+The [`render`](/docs/api.md#render) event is fired before the [view](/docs/view.md) function is called, allowing you to overwrite it or decorate it. If your application does not use a view, this event is never fired.
```jsx
app({
- view: state =>
Hi.
,
events: {
- render(state, actions) {
- if (location.pathname === "/warp") {
- return state =>
Welcome to warp zone!
- }
+ render(state, actions, view) {
+ return location.pathname === "/" ? defaultView : notFoundView
}
}
})
@@ -64,19 +112,20 @@ app({
## Custom Events
-Create custom events using the [`emit`](/docs/api.md#emit) function.
+Create custom events with the [`emit`](/docs/api.md#emit) function.
```jsx
emit("myEvent", data)
```
-Then subscribe to them in your application or [mixin](/docs/mixins.md).
+Then subscribe to them like any other event.
```jsx
app({
events: {
myEvent(state, actions, data) {
- // return new data
+ // ...
+ return newData
}
}
})
@@ -85,34 +134,37 @@ app({
The `emit` function is available as the return value of the [`app`](/docs/api.md#app) function call itself.
```js
-const emit = app({ ... })
+const emit = app({
+ // ...
+})
```
Or in mixins, as the first argument to the function.
```js
-const MyMixin = emit => ({ ... })
+function MyMixin(emit) {
+ // ...
+}
```
-The `emit` function returns the supplied data reduced by successively calling each event handler of the specified event.
+The `emit` function returns the supplied data, reduced by successively calling each event handler of the specified event.
-### Interoperatiblity
+### Interoperability
Custom events can be useful in situations where your application is a part of a larger system and you want to communicate with it from the outside.
```js
const emit = app({
- ...
events: {
- externalEvent: (state, actions, data) => actions.setData(data)
+ externalEvent(state, actions, data) {
+ actions.populate(data)
+ }
}
})
-...
+// ...
-emit("externalEvent", {
- data: 42
-})
+emit("externalEvent", yourData)
```
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 62d1a0731..fc8a17fca 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -12,8 +12,10 @@ Let's begin with the simplest of all programs. Paste the following code in a new
const { h, app } = hyperapp
app({
- state: "Hello.",
- view: state => h("h1", {}, state)
+ state: {
+ message: "Hello."
+ },
+ view: state => h("h1", {}, state.message)
})
@@ -22,31 +24,31 @@ app({
You should see that "Hello." is displayed on the page.
+### Dissecting the Code
+
The state describes the application's data.
```js
-state: "Hello."
+state: {
+ message: "Hello."
+}
```
The view describes the application's user interface.
```js
-state => h("h1", {}, state)
+state => h("h1", {}, state.message)
```
-You can write a view using [JSX] or [Hyperx] and compile it in a [build pipeline](#build-pipeline).
+You can write a view using [JSX], [hyperx], etc., and compile it in a [build pipeline](#build-pipeline).
```jsx
-state =>
{state}
+state =>
{state.message}
```
-The [app](/docs/api.md#app) function wraps up everything and renders the view on the DOM.
-
-And... we're done.
+The [app](/docs/api.md#app) function wraps it all together and renders the view on the DOM.
----
-
-We've only scratched the surface of what you can do and what's available in HyperApp. To learn more, check out the [Tutorials](/docs/tutorials.md) or read the [Implementation Notes](/docs/implementation-nodes.md) to peek under the hood.
+To learn more, check out the [Tutorials](/docs/tutorials.md) or read the [Implementation Notes](/docs/implementation-nodes.md) to peek under the hood.
## Installation
@@ -78,24 +80,24 @@ import { h, app } from "hyperapp"
A build pipeline typically consists of a package manager, a compiler and a bundler.
-Using a build pipeline we can transform JSX / Hyperx markup into [h](/docs/api.md#h) calls before runtime. This is much faster than sending a parser down the wire and compiling the view in the browser.
+Using a build pipeline we can transform JSX or hyperx markup into [`h`](/docs/api.md#h) calls before runtime. This is much faster than sending a parser down the wire and compiling the view in the browser.
-JSX / Hyperx in:
+JSX or hyperx
```jsx
Hello.
```
-Vanilla out:
+Vanilla out
```jsx
h("main", { id: "app" }, "Hello.")
```
-A build pipeline lets you easily install and update third-party libraries, compile modern JavaScript for older browser and bundle your application into small modules to optimize load time.
+A build pipeline lets you install and update third-party libraries easily, compile modern JavaScript for older browser and bundle your application into small modules to optimize load time.
-See [JSX] or [Hyperx] for setup instructions.
+See [JSX] or [hyperx] for setup instructions.
-[Hyperx]: /docs/hyperx.md
+[hyperx]: /docs/hyperx.md
[JSX]: /docs/jsx.md
-
+[t7]: https://github.com/trueadm/t7
diff --git a/docs/gif-search.md b/docs/gif-search.md
index b4b44cbc1..9f6f29db7 100644
--- a/docs/gif-search.md
+++ b/docs/gif-search.md
@@ -2,7 +2,7 @@
In this example we'll use the [Giphy API](https://api.giphy.com/) to create a GIF search and learn how to update the state asynchronously.
-[Try it online](https://codepen.io/hyperapp/pen/ZeByKv?editors=0010)
+[Try it Online](https://codepen.io/hyperapp/pen/ZeByKv?editors=0010)
```jsx
app({
@@ -90,7 +90,3 @@ fetch(
```
Finally, call `actions.toggleFetching` to allow further fetch requests to be made and update the state by passing the fetched GIF URL to `actions.setUrl`.
-
-
-
-[Back to Tutorials](/docs/tutorials.md)
diff --git a/docs/hyperx.md b/docs/hyperx.md
index c0b69e984..43e740eee 100644
--- a/docs/hyperx.md
+++ b/docs/hyperx.md
@@ -15,11 +15,11 @@ const main = html`
## Setup
-We can use [Hyperxify](https://github.com/substack/hyperxify) to transform Hyperx into [`h`](/docs/h.md#h) function calls and a bundler to create a single file we can deliver to the browser.
+We can use [hyperxify](https://github.com/substack/hyperxify) to transform hyperx into [`h`](/docs/h.md#h) function calls and a bundler to create a single file we can deliver to the browser.
-The ES6 import syntax is incompatible with Hyperxify, so we'll use the Node.js require function.
+The ES6 import syntax is incompatible with hyperxify, so we'll use the Node.js require function.
-In a new directory, create an `index.html` file:
+In a new directory, create an `index.html` file.
```html
@@ -32,7 +32,7 @@ In a new directory, create an `index.html` file: