A Tutorial On How To Build An Application With Hyperapp — Part 2
What Will You Learn?
In this two-part tutorial series, you will learn to build a simple application with a new frontend library called Hyperapp. Hyperapp is a new promising frontend library. It lets you develop reasonably quickly without the need to learn too many new concepts. Most of the material will be familiar to you if you know one of the current frontend technologies like Vue.js, React, or Angular.
This article is part 2 of the tutorial. If you did not read the first part, now would be a good time to do so.
Curriculum
- Build a starter project for Hyperapp
- Build a simple application using SteemJS on top of the starter we built in part one
In part one, you learned to …
- build a starter for your next Hyperapp projects
- use Parcel to bundle your Hyperapp application
- use the starter in development mode
In part two, this articles, you will learn to …
- build a simple application on top of the Hyperapp starter
- style your application with Picostyle
- manage application state with Hyperapp's state management
- publish your application to the interwebs
Requirements
You should know how to write simple JS apps. Concepts from React, Redux, or Elm are also beneficial, but not necessary.
Difficulty
The tutorial should be relatively easy to follow. But if you do not know JavaScript another tutorial might be better before going through this material.
The core technologies used in this tutorial series
- JavaScript
- Knowledge on using the command line interface
- Hyperapp is a 1 KB JavaScript library for building frontend applications
- Picostyle is a 0.4 KB CSS-in-JS library
- Parcel is a blazing fast, zero configuration web application bundler
- SteemJS JavaScript Library
Building A Simple Application On Top Of The Hyperapp Starter
In the first part of this tutorial, we built a simple starter application with Hyperapp and Parcel. The starter is called HyperParcel and is available on GitHub.
In this part, we will learn how to write a simple application which uses the SteemJS library. We will use the HyperParcel starter as a basis for the new app. When the app is finished, you will be able to enter a Steemit username, and the program will retrieve the reputation of that user. In Steemit you can see a user's status easily. It is the number in parenthesis after the username. Our app will show the same number but will also show the decimal place. So we can now see how close we are to the next jump in reputation.
You will also learn how to calculate the reputation.
Let's get started.
Clone the starter into a new folder called hyper-steem
:
git clone https://github.com/cutemachine/hyperparcel.git steem-reputation
Then remove the old git history from the cloned project:
cd hyper-steem
rm -rf .git
Now you can initialize a new Git repo. You can skip this part if you do not want to put your code under version control.
git init
git add .
git commit -m 'Initial version of steem-reputation'
To be able to run the new application we need to install it's dependencies first:
yarn
Now start the app and point your browser to http://localhost:1234
yarn start
What you see is a simple app which shows a count you can increase or decrease. Nothing too exciting. If you worked through the first part, you would notice that the starter is now styled.
If you look inside the package.json
file you can see that there is a new package called picostyle. We will use Picostyle to style our application. It is much easier to use than SASS or LESS. It is a CSS-in-JS library, so you have all the power of CSS and JavaScript combined to style your program.
Let's find out how you can style a simple component. In index.js
we import a component called H1. You can find it in src/components/H1/index.js
. It looks like this:
import { h, app } from "hyperapp"
import picostyle from "picostyle"
const style = picostyle(h)
export default style('h1')({
fontSize: '5em',
textAlign: 'center',
color: 'palevioletred'
})
As you can see we first import the picostyle library. Then we wire it up to the h-function from the Hyperapp library.
Then we can use the style function to style an HTML element. The first argument you pass to the style
function is an object with the actual styles. You need to write the properties without hyphens. So font-size
becomes fontSize
. The values need to be enclosed in apostrophes or quotation marks.
Go ahead and make some changes to the component. First, ensure that you started the application with yarn start
. Then in src/components/H1/index.js
change the color from palevioletred
to #f08
. Ass soon as you save your changes you should see the update in the browser. There is no need to reload your app. Cool, isn't it.
Take also a look at the other files in the components/
folder.
A Fresh Start
Now, that we have a basic understanding of Picostyle we will remove the parts from the starter we do not need.
You can remove the following files from the starter:
src/components/Button/index.js
src/components/Flex/index.js
src/components/Wrapper/index.js
src/index.js
Keep src/components/H1/index.js
as we will use it in our new application.
New Code
In this section, you will add four new source files. After each code section, I will discuss what is new and noteworthy.
src/components/Main/
src/components/Input/
src/components/FlexColumn/
src/index.js
Main Component
import { h, app } from "hyperapp"
import picostyle from "picostyle"
const style = picostyle(h)
export default style('main')({
padding: '4em',
background: '#60D2AC',
height: '100vh',
"@media (max-width: 70em)": {
fontSize: "0.6em",
},
})
Here we create a styled component which will render the HTML tag main
. We will use it later in the src/index.js
file to wrap all other elements. The only thing worth mentioning here is the media query. See how one needs to enclose the property in quotation marks?
Input Component
import { h, app } from 'hyperapp'
import picostyle from "picostyle"
const style = picostyle(h)
export default style('input')({
width: '100%',
fontSize: '5em',
color: '#f08',
background: '#60D2AC',
border: 'none',
borderBottom: '0.05em solid #f08',
outline: 'none',
textAlign: 'center',
})
The Input component is just a styled version of the HTML input
element. We will use it in src/index.js
to allow a user to enter a Steemit username.
FlexColumn Component
import { h, app } from "hyperapp"
import picostyle from "picostyle"
const style = picostyle(h)
export default style('div')({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
maxWidth: '50em',
marginLeft: 'auto',
marginRight: 'auto',
})
This code creates a wrapper around the other components we use in src/index.js
. Its purpose is to center the various elements (H1, Input, etc.).
The Application File — index.js
import { h, app } from "hyperapp"
import steem from 'steem'
import Main from './components/Main'
import FlexColumn from './components/FlexColumn'
import H1 from './components/H1'
import Input from './components/Input'
import './reset.css'
require('babel-polyfill')
const state = {
error: '',
user: '',
reputation: ''
}
const formatReputation = (reputation, digits = 3) => {
const isNegative = (reputation < 0)
let formattedReputation = Math.log10(Math.abs(reputation))
formattedReputation = Math.max(formattedReputation - 9, 0)
formattedReputation *= isNegative ? -9 : 9
formattedReputation += 25
return formattedReputation.toFixed(digits)
}
const actions = {
getAccount: () => async (state, actions) => {
try {
const [account] = await steem.api.getAccountsAsync([state.user])
if (!account) { throw new Error('Sorry, no account found. Minimum 3 chars, no uppercase.')}
actions.reputation(account.reputation)
} catch (error) {
actions.error(error.message)
}
},
error: message => state => ({ error: message }),
reputation: value => state => ({ reputation: formatReputation(value, 5)}),
updateUser: ({ user }) => state => {
return { user: user, error: '', reputation: '' }
},
submitUser: () => (state, actions) => {
actions.getAccount()
}
}
const view = (state, actions) => (
<Main>
<FlexColumn>
<H1>Steemit Reputation</H1>
<Input
onkeyup={
event => {
event.keyCode === 13 ?
actions.submitUser() :
actions.updateUser({ user: event.target.value })
}
}
placeholder='username …'
autofocus
/>
<p>{state.error}</p>
<H1 style={{marginTop: '1em'}}>{state.reputation}</H1>
</FlexColumn>
</Main>
)
const main = app(state, actions, view, document.body)
This file is where the music is playing :) This is the whole application.
It would be tempting to try to start the app now. But we need to take care of a few things first. As you can see in the above code, we are importing the steem
library. To make this import statement work we need to add the dependency first. Otherwise, you will get an error like this when you start the app with yarn start
:
🚨 /Projects/Playground/steem-reputation/src/index.js: Cannot resolve dependency 'steem'
To add the Stemm library use the following command:
yarn add steem
When the installation succeeded you should see the following dependency in your package.json
:
"dependencies": {
"hyperapp": "^1.0.2",
"picostyle": "^0.2.1",
"steem": "^0.7.1"
}
Still, we cannot start the app. There is another dependency missing. As you can see in the code above we require a library called babel-polyfill
. This lib will allow us to use async / await in our code. Without the dependency you would see the following error:
🚨 /Projects/Playground/steem-reputation/src/index.js:8:8: Cannot resolve dependency 'babel-polyfill'
6 | import Input from './components/Input'
7 | import './reset.css'
8 | require('babel-polyfill')
Please install the babel-polyfill
lib with the following command:
yarn add babel-polyfill
Fine. Now we are finally ready to install all packages and start the application.
yarn
yarn start
When you point your browser to localhost:1234
you should see the running app.
TODO: Add image
Code Walk-Through
This is a short explanation of what is going on in src/index.js
.
Application State
We store all application state in store called state.
const state = {
error: '',
user: '',
reputation: ''
}
The state will be passed to the view. We use state.reputation
to display the formatted reputation score of the user. state.error
is used to show any error messages we might get when calling steem.api.getAccountsAsync
. We catch the error that the network might be down or that a user does not exist on the Steem blockchain.
Actions
When we need to change the state of our application, we will do it through actions. We can also have asynchronous actions, like the getAccount
action, which will call the steem.api.getAccountsAsync
function. We do not know how long the request will take, therefore we mark getAccount
with the async
keyword.
If you never used async / await, I recommend you read this great article. For this tutorial, you only need to know that await
in the getAccount
action will wait till we receive an answer from the API call. If we get undefined, it means there is no account for the given username. If we get an error, we will dispatch the error action and fill the error state with the error message.
If the API call was successful, we have the account data and can dispatch the reputation action with the value from account.reputation
. A reputation is a big number which needs to be formatted so that we have it in a readable format. The formatting is done in the formatReputation
function.
const actions = {
getAccount: () => async (state, actions) => {
try {
const [account] = await steem.api.getAccountsAsync([state.user])
if (!account) { throw new Error('Sorry, no account found. Minimum 3 chars, no uppercase.')}
actions.reputation(account.reputation)
} catch (error) {
actions.error(error.message)
}
},
error: message => state => ({ error: message }),
reputation: value => state => ({ reputation: formatReputation(value, 5)}),
updateUser: ({ user }) => state => {
return { user: user, error: '', reputation: '' }
},
submitUser: () => (state, actions) => {
actions.getAccount()
}
}
There are two more actions which are powering our app, updateUser
and submitUser
. updateUser
will take the value from the input form and stores it in the state object. When the user has entered a username and hits return (keycode 13) the submitUser
action will be called. The submitUser
action will call the getAccoun
t action.
And that's it.
You can see a running version of the application on the interwebs.
Deployment
Deployment is quite comfortable with Netlify:
yarn build
netlify deploy
? No site id specified, create a new site Yes
? Path to deploy? (current dir) dist
Deploying folder: dist
Conclusion
As you just saw, it is quite easy to start your webapp with HyperParcel. Just clone the starter. Remove the initial example and start coding your app.
Best,
Jo
Posted on Utopian.io - Rewarding Open Source Contributors
Thank you for the contribution. It has been approved.
You can contact us on Discord.
[utopian-moderator]
Many thanks, @roj.
Really good tutorial @cutemachine. Very comprehensive and easy to understand. Picostyle is a new one for me. More research to add to my list!
You ever used Semantic UI? I’ve been playing around with it as a replacement for Bootstrap. Much more logical class names and quite lightweight too.
Thanks Steve for your kind words. Really appreciate your feedback.
Picostyle is interesting, but you do not have to use it. You can use Hyperapp with Semantic UI as well. I'm currently working on a starter project which uses Hyperapp with Bulma CSS.
Yes, I worked with Semantic UI in the past and liked it a lot. There is also a React version of it, which I have to try yet.
Bulma looks really good. I’ll give it a shot. I’m always looking for tools that can fill the gaps - my students appreciate styling frameworks that are easier to learn and implement than Bootstrap.
Upvote and comment my post i will upvote and comment your post
Hey @cutemachine I am @utopian-io. I have just upvoted you!
Achievements
Suggestions
Get Noticed!
Community-Driven Witness!
I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!
Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x
This post has received a 19.61% upvote from @lovejuice thanks to @cutemachine. They love you, so does Aggroed. Please be sure to vote for Witnesses at https://steemit.com/~witnesses.
You got a 2.06% upvote from @upmyvote courtesy of @cutemachine!
If you believe this post is spam or abuse, please report it to our Discord #abuse channel.
If you want to support our Curation Digest or our Spam & Abuse prevention efforts, please vote @themarkymark as witness.
You got a 1.15% upvote from @buildawhale courtesy of @cutemachine!
If you believe this post is spam or abuse, please report it to our Discord #abuse channel.
If you want to support our Curation Digest or our Spam & Abuse prevention efforts, please vote @themarkymark as witness.
Thank you cutemachine for making a transfer to me for an upvote of 13.89% on this post!
Half of your bid goes to @budgets which funds growth projects for Steem like our top 25 posts on Steem!
The other half helps holders of Steem power earn about 60% APR on a delegation to me!
For help, will you please visit https://jerrybanfield.com/contact/ because I check my discord server daily?
To learn more about Steem, will you please use http://steem.guide/ because this URL forwards to my most recently updated complete Steem tutorial?
Resteem to 700+ Follower . Send 0.150 SBD or 0,250 STEEM to @music-curation ( URL as memo )