Formatting, typos, minor refactoring

This commit is contained in:
Jorge Bucaran 2020-07-01 01:38:17 +09:00 committed by GitHub
parent 0eb6b0f662
commit f1b6de9c90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 89 additions and 88 deletions

View File

@ -164,55 +164,57 @@ of virtual nodes. Hyperapp proceeds to create it for us, replacing the node spec
To render the HTML we want, change the `view` to:
```js
view: () => h("div", {id: "app", class: "container"}, [
h("div", {class: "filter"}, [
" Filter: ",
h("span", {class: "filter-word"}, "ocean"),
h("button", {}, "\u270E")
]),
h("div", {class: "stories"}, [
h("ul", {}, [
h("li", {class: "unread"}, [
h("p", {class: "title"}, [
"The ",
h("em", {}, "Ocean"),
" is Sinking!"
view: () =>
h("div", { id: "app", class: "container" }, [
h("div", { class: "filter" }, [
" Filter: ",
h("span", { class: "filter-word" }, "ocean"),
h("button", {}, "\u270E"),
]),
h("div", { class: "stories" }, [
h("ul", {}, [
h("li", { class: "unread" }, [
h("p", { class: "title" }, [
"The ",
h("em", {}, "Ocean"),
" is Sinking!",
]),
h("p", { class: "author" }, "Kat Stropher"),
]),
h("li", { class: "reading" }, [
h("p", { class: "title" }, [h("em", {}, "Ocean"), " life is brutal"]),
h("p", { class: "author" }, "Surphy McBrah"),
]),
h("li", {}, [
h("p", { class: "title" }, [
"Family friendly fun at the ",
h("em", {}, "ocean"),
" exhibit",
]),
h("p", { class: "author" }, "Guy Prosales"),
]),
h("p", {class: "author"}, "Kat Stropher")
]),
h("li", {class: "reading"}, [
h("p", {class: "title"}, [
h("em", {}, "Ocean"),
" life is brutal"
]),
h("p", {class: "author"}, "Surphy McBrah"),
]),
h("li", {}, [
h("p", {class: "title"}, [
"Family friendly fun at the ",
h("em", {}, "ocean"),
" exhibit"
]),
h("p", {class: "author"}, "Guy Prosales")
])
])
]),
h("div", {class: "story"}, [
h("h1", {}, "Ocean life is brutal"),
h("p", {}, `
]),
h("div", { class: "story" }, [
h("h1", {}, "Ocean life is brutal"),
h(
"p",
{},
`
Lorem ipsum dolor sit amet, consectetur adipiscing
elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip
ex ea commodo consequat.
`),
h("p", {class: "signature"}, "Surphy McBrah")
]),
h("div", {class: "autoupdate"}, [
"Auto update: ",
h("input", {type: "checkbox"})
`
),
h("p", { class: "signature" }, "Surphy McBrah"),
]),
h("div", { class: "autoupdate" }, [
"Auto update: ",
h("input", { type: "checkbox" }),
]),
])
]),
```
Try it out to confirm that the result matches the screenshot above.
@ -234,13 +236,11 @@ Add this function (in the "VIEWS" section):
```js
const emphasize = (word, string) =>
string.split(" ").map(x => {
if (x.toLowerCase() === word.toLowerCase()) {
return h("em", {}, x + " ")
} else {
return x + " "
}
})
string
.split(" ")
.map((x) =>
x.toLowerCase() === word.toLowerCase() ? h("em", {}, x + " ") : x + " "
)
```
It lets you change this:
@ -269,7 +269,7 @@ Story thumbnails are repeated several times, so encapsulate
them in their own function:
```js
const storyThumbnail = props =>
const storyThumbnail = (props) =>
h(
"li",
{
@ -292,12 +292,12 @@ const storyThumbnail = props =>
Continue by creating functions for each section of the view:
```js
const storyList = props =>
const storyList = (props) =>
h("div", { class: "stories" }, [
h(
"ul",
{},
Object.keys(props.stories).map(id =>
Object.keys(props.stories).map((id) =>
storyThumbnail({
id,
title: props.stories[id].title,
@ -310,14 +310,14 @@ const storyList = props =>
),
])
const filterView = props =>
const filterView = (props) =>
h("div", { class: "filter" }, [
"Filter:",
h("span", { class: "filter-word" }, props.filter),
h("button", {}, "\u270E"),
])
const storyDetail = props =>
const storyDetail = (props) =>
h("div", { class: "story" }, [
props && h("h1", {}, props.title),
props &&
@ -335,13 +335,13 @@ const storyDetail = props =>
props && h("p", { class: "signature" }, props.author),
])
const autoUpdateView = props =>
const autoUpdateView = (props) =>
h("div", { class: "autoupdate" }, [
"Auto update: ",
h("input", { type: "checkbox" }),
])
const container = content => h("div", { class: "container" }, content)
const container = (content) => h("div", { class: "container" }, content)
```
With those the view can be written as:
@ -442,7 +442,7 @@ the pencil-button, a text input with the filter word appears.
Add an `onclick` property to the button in `filterView`:
```js
const filterView = props =>
const filterView = (props) =>
h("div", { class: "filter" }, [
"Filter:",
h("span", { class: "filter-word" }, props.filter),
@ -455,7 +455,7 @@ that when the button is clicked, an action named `StartEditingFilter` is
_dispatched_. Create the action in the "ACTIONS" section:
```js
const StartEditingFilter = state => ({ ...state, editingFilter: true })
const StartEditingFilter = (state) => ({ ...state, editingFilter: true })
```
Actions are just functions describing transformations of the state.
@ -471,7 +471,7 @@ span with the filter word. We can express this in `filterView` using a
ternary operator (`a ? b : c`).
```js
const filterView = props =>
const filterView = (props) =>
h("div", { class: "filter" }, [
"Filter:",
@ -489,13 +489,13 @@ a way to go back. We need an action to `StopEditingFilter`, and a button to disp
Add the action:
```js
const StopEditingFilter = state => ({ ...state, editingFilter: false })
const StopEditingFilter = (state) => ({ ...state, editingFilter: false })
```
and update `filterView` again:
```js
const filterView = props =>
const filterView = (props) =>
h("div", { class: "filter" }, [
"Filter:",
@ -521,7 +521,7 @@ type in the box should be emphasized in the story-list.
Update `filterView` yet again:
```js
const filterView = props =>
const filterView = (props) =>
h("div", { class: "filter" }, [
"Filter:",
@ -567,7 +567,7 @@ It has a payload, but it's not an event object. It's a custom value telling us w
story was clicked. How are actions dispatched with custom payloads?  Like this:
```js
const storyThumbnail = props =>
const storyThumbnail = (props) =>
h(
"li",
{
@ -621,7 +621,7 @@ But we don't know the word beforehand, so how can we set it as a custom payload?
again (last time - I promise!):
```js
const filterView = props =>
const filterView = (props) =>
h("div", { class: "filter" }, [
"Filter:",
@ -629,7 +629,7 @@ const filterView = props =>
? h("input", {
type: "text",
value: props.filter,
oninput: [SetFilter, event => event.target.value], // <----
oninput: [SetFilter, (event) => event.target.value], // <----
})
: h("span", { class: "filter-word" }, props.filter),
@ -662,7 +662,7 @@ const GotStories = (state, stories) => ({
// replace old stories with new,
// but keep the 'seen' value if it exists
stories: Object.keys(stories)
.map(id => [
.map((id) => [
id,
{
...stories[id],
@ -685,7 +685,7 @@ besides transforming the state, that "something" is called an _effect_. To assoc
with `StopEditingFilter`, make it return an array like this:
```js
const StopEditingFilter = state => [
const StopEditingFilter = (state) => [
{
...state,
editingFilter: false,
@ -702,7 +702,7 @@ has been updated.
Add this effect declaration:
```js
const StopEditingFilter = state => [
const StopEditingFilter = (state) => [
{
...state,
editingFilter: false,
@ -732,8 +732,8 @@ Now we just need to implement `fetchJSONData`. Type this in the "EFFECTS & SUBSC
```js
const fetchJSONData = (dispatch, options) =>
fetch(options.url)
.then(response => response.json())
.then(data => dispatch(options.onresponse, data))
.then((response) => response.json())
.then((data) => dispatch(options.onresponse, data))
.catch(() => dispatch(options.onresponse, {}))
```
@ -790,7 +790,7 @@ However, repeating the effect declaration in all its gory detail like this
is not ideal, so lets add this _effect creator_
```js
const storyLoader = searchWord => [
const storyLoader = (searchWord) => [
fetchJSONData,
{
url: `https://hyperapp.dev/tutorial-assets/stories/${searchWord.toLowerCase()}.json`,
@ -802,7 +802,7 @@ const storyLoader = searchWord => [
Now we can simplify `StopEditingFilter` like this:
```js
const StopEditingFilter = state => [
const StopEditingFilter = (state) => [
{
...state,
editingFilter: false,
@ -837,7 +837,7 @@ const SetFetching = (state, fetching) => ({ ...state, fetching })
Update `storyLoader` to tell `fetchJSONData` about `SetFetching`
```js
const storyLoader = searchWord => [
const storyLoader = (searchWord) => [
fetchJSONData,
{
url: `https://hyperapp.dev/tutorial-assets/stories/${searchWord.toLowerCase()}.json`,
@ -854,8 +854,8 @@ Finally update `fetchJSONData` to use the new `onstart` and `onfinish` options t
const fetchJSONData = (dispatch, options) => {
dispatch(options.onstart) // <---
fetch(options.url)
.then(response => response.json())
.then(data => dispatch(options.onresponse, data))
.then((response) => response.json())
.then((data) => dispatch(options.onresponse, data))
.catch(() => dispatch(options.onresponse, {}))
.finally(() => dispatch(options.onfinish)) // <---
}
@ -865,7 +865,7 @@ With that, our state prop `fetching` will always tell us wether or not we are fe
Use that to show a spinner when we are fetching, in `storyList`:
```js
const storyList = props =>
const storyList = (props) =>
h("div", { class: "stories" }, [
// show spinner overlay if fetching
props.fetching &&
@ -874,7 +874,7 @@ const storyList = props =>
h(
"ul",
{},
Object.keys(props.stories).map(id =>
Object.keys(props.stories).map((id) =>
storyThumbnail({
id,
title: props.stories[id].title,
@ -906,13 +906,16 @@ but you'll know it's happening when you see the spinner pop up every five second
First let's keep track of wether or not the user wants this auto-update feature on. Create a new action:
```js
const ToggleAutoUpdate = state => ({ ...state, autoUpdate: !state.autoUpdate })
const ToggleAutoUpdate = (state) => ({
...state,
autoUpdate: !state.autoUpdate,
})
```
Dispatch it in response to checking the checkbox in `autoUpdateView`:
```js
const autoUpdateView = props =>
const autoUpdateView = (props) =>
h("div", { class: "autoupdate" }, [
"Auto update: ",
h("input", {
@ -928,17 +931,17 @@ With that, the state property `autoUpdate` will tell us wether or not the Auto-u
### Subscription functions <a name="subscriptionfunctions"></a>
We need a _subscription function_ capable of dispatching actions at a given interval. Implement
`intervalSubscription` in the "EFFECTS & SUBSCRIPTIONS" section:
`intervalSub` in the Effects & Subscriptions section:
```js
const intervalSubscription = (dispatch, options) => {
const interval = setInterval(() => dispatch(options.action), options.time)
const intervalSub = (dispatch, props) => {
const interval = setInterval(() => dispatch(props.action), props.time)
return () => clearInterval(interval)
}
```
Just like an effect function, this function will be called by Hyperapp with `dispatch` and given options. It
will start an interval listener, and every `options.time` milliseconds, it will dispatch the given action. The
Just like an effect function, this function will be called by Hyperapp with `dispatch` and given props. It
will start an interval listener, and every `props.time` milliseconds, it will dispatch the given action. The
main difference to an effect function is that a subscription function returns a function so hyperapp knows
how to stop the subscription.
@ -951,10 +954,10 @@ We could create a new action for updating stories, but since `StopEditingFilter`
use it here too. Add a `subscription` property to the app:
```js
subscriptions: state => [
subscriptions: (state) => [
state.autoUpdate &&
!state.editingFilter && [
intervalSubscription,
intervalSub,
{
time: 5000, //milliseconds,
action: StopEditingFilter,
@ -978,6 +981,4 @@ If you'd like to see a working example of the final code, have a look [here](htt
Congratulations on completing this Hyperapp tutorial!
Along the way you've familiarized yourself with
the core concepts: _view_, _state_, _actions_, _effects_ & _subscriptions_. And that's really all you need to
build any web application.
Along the way you've familiarized yourself with the core concepts: _view_, _state_, _actions_, _effects_ & _subscriptions_. And that's really all you need to build any web application.