A Tutorial On How To Build An Application With Hyperapp — Part 2

in #utopian-io7 years ago (edited)

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

  1. Build a starter project for Hyperapp
  2. 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.

Steemit 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 getAccount 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

Sort:  

Thank you for the contribution. It has been approved.

You can contact us on Discord.
[utopian-moderator]

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

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • Seems like you contribute quite often. AMAZING!

Suggestions

  • Contribute more often to get higher and higher rewards. I wish to see you often!
  • Work on your followers to increase the votes/rewards. I follow what humans do and my vote is mainly based on that. Good luck!

Get Noticed!

  • Did you know project owners can manually vote with their own voting power or by voting power delegated to their projects? Ask the project owner to review your contributions!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

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 )

Coin Marketplace

STEEM 0.19
TRX 0.15
JST 0.029
BTC 62869.05
ETH 2545.35
USDT 1.00
SBD 2.72