Closed jorgebucaran closed 6 years ago
First of all I think it is important to have short installation instruction (getting started) and browser support list on the front page (the main README.md I mean). Also could be useful to have documentation TOC there too, make introduction for users as useful as possible, you still may keep readme short by using [more] links to docs.
Few uncovered things in docs I can imagine right now:
How to display the same thing twice (reusable views/components/apps). For example counter could be used twice in the header and footer of the page.
import { h, app } from 'hyperapp'
import {
state as counterState,
actions as counterActions,
view as counterView
} from './counter' // some crazy naming conventions are needed?
app(
{ // state
counterA: counterState,
counterB: { ...counterState }, // must be a deep copy? what the best way to clone
},
{ // actions
counterA: counterActions,
counterB: counterActions,
},
(state, actions) => ( // view
<main>
{counterView(state.counterA, actions.counterA)}
<hr/>
{counterView(state.counterB, actions.counterB)}
{/*
or it is better to pass props manually?
<CounterView count={state.counterB.count} up={actions.counterB.up} />
<counterView /> does not work because of jsx naming requirement?
*/}
</main>
),
document.body
)
state.location
to highlight a button based on current location.Having recently read several projects docs as a newbie I must say Vue have been the quickest to digest and left me with the least number of open questions.
How to create reusable modules, how to share, how to make ensure it doesn't break someone else's app if they use your module.
The open questions I have for Hyper App having read around and looked at the samples are
Hope that helps
I would like a more detailed explanation of hydration, the current docs don't make it clear how you should set up html to be used by hyperapp.
Since the library is so small and I understand a major goal is to keep it that way I would like to see annotated source code like for underscore.js. This will greatly help new users/contributors dive into the codebase and figure out how things work or identify bugs.
@timjacobi That's really cool! I never knew that was a thing haha ๐
@Ch4s3 How much clear the hydration docs would need to be? The process is extremely simple, hence the extremely simple docs. What (part) wasn't clear?
@JorgeBucaran Yeah, I think I get the idea, but in practice it didn't seem to work when I tried it. The example html in the docs is presently:
<html>
<head>
<script defer src="bundle.js"></script>
</head>
<body>
<main>
<h1>0</h1>
<button>โ</button>
<button>+</button>
</main>
</body>
</html>
Should the contents of bundle.js
be exactly what's in the Hello World example?
import { h, app } from "hyperapp"
const state = {
count: 0
}
const actions = {
down: () => state => ({ count: state.count - 1 }),
up: () => state => ({ count: state.count + 1 })
}
const view = (state, actions) => (
<main>
<h1>{state.count}</h1>
<button onclick={actions.down}>-</button>
<button onclick={actions.up}>+</button>
</main>
)
export const main = app(state, actions, view, document.body)
Can I render to just a div that contains <main></main>
and not replace the whole page body? Say for example my hyperapp is just a component of an otherwise mostly plain SSR html page, and I want to prerender the html and have HyperApp take over that div, will it work?
@Ch4s3 Should the contents of bundle.js be exactly what's in the Hello World example?
Yes.
Can I render to just a div that contains
<main></main>
and not replace the whole page body?
Yes. The given example replaces the page body because the given container is document.body
, but you can target a different container, that's why there isn't a default one.
// This is your server side rendered HTML.
document.body.innerHTML = `
<div id="app"><main><p>foo</p></main></div>
`
// And this is in your bundle.js
app(
state,
actions,
state => h("main", {}, [h("p", {}, "foo")]),
document.getElementById("app") // <= Any container you want!
)
I want to prerender the html and have ~HyperApp~ Hyperapp take over that div, will it work?
Yes. Imagine your SSR HTML looks like this:
<div>
<div id="reactapp">
...
</div>
<div id="vueapp">
...
</div>
<div id="hyperapp">
<main>
<h1>0</h1>
<button>โ</button>
<button>+</button>
</main>
</div>
</div>
All you need is pass document.getElementById("hyperapp")
as a container to the app() call.
Awesome, thanks @JorgeBucaran.
Hey @Ch4s3 how about a PR adding that info to the docs? :)
@SteveALee If it can be better re-worded and re-written, that would be ๐ฏ
Working examples with playground like Try Elm which is centrally/regularly updated, because examples allover the internet are mostly non-working... Architecture diagrams, tutorials, video tutorials. Yes I know how much resources this requires... Just saying ๐
@kosirm Thanks for your feedback. Do you have something more specific in mind? The question is what kind of documentation would you like to see on this repo, but your suggestion sounds more like the kind of learning resources you wish were available, which is fine, but not the kind of feedback I am trying to collect.
Oh, I see Higher Order Apps (wrappers?) are a thing. Need documenting
@JorgeBucaran
your suggestion sounds more like the kind of learning resources you wish were available
sorry for misunderstanding topic... Thanks for understanding ๐
I have been using hyperapp
for a while now, and I find the current documentation to be more than good enough. It's simple and to the point, as it should be. So thanks @JorgeBucaran and the rest.
What I would like to see, is a "cookbook" style of document. What I mean by that is e.g the usage of a helper getState
function in actions
documented. Such minor real-life applications can be most helpful, especially to newcomers. I am not certain about instructing the separation of modules in a certain way for scalability, as that's very dependent upon the domain itself.
So my suggestion basically boils down to the addition of a separate document, where all common tasks related to various use-cases can be appended incrementally. Thus, alleviating the pain of developers coming from other frameworks, as well as those accustomed to non-functional programming paradigms.
BTW, state mutation is bad, should it be allowed in the awesome list?
Thanks @kenOfYugen! ๐
They caught you @marcusasplund! ๐ ๐
@kenOfYugen Thx for the heads up, please review
Maybe something on the approach to node checking (#378)? I imagine something small in the Virtual Nodes or Component section would suffice.
When no validation is needed, I might as well use jQuery events for forms.
I think it would be helpful to mention that the return value of app() is the wiredActions object. I didn't want to pass all the actions down the tree and in the code I found that I can just use the result of app() to export this object and use these wired actions in my components via importing them.
Also an example for this would be helpful. At least I couldn't find that in the docs.
@mightyplow I admit it could be much better, but it's there. :)
@JorgeBucaran sorry, my bad. Well done! ;)
Explain the different type of actions
, why are async actions wrapped in an extra function?
because actions are usually just event handlers like
onclick={action}
and we don't know with which amount of arguments it will be called, maybeaction(event)
or maybeaction(a, b, c)
, where the state argument should go? - @frenzzy
@Siilwyn This should be better documented, but API symmetry is one of the reasons. I want to call actions the same way that I define them.
const actions: {
myAction: data => ...
}
so
actions.myAction(data)
But then why do actions take only one argument? What if one were to pass extra parameters?
@infinnie, to pass multiple values to an action currently, you will need to pass them as an object.
@infinnie I'm not sure if you're familiar with Redux, but there's some similarities between the actions from Redux and ours.
Hyperapp actions don't require a type
property since they are referenced to by the name of the property they are attached to within the actions
object.
However to someone who is not a Redux user that would just seem unintuitive.
But then why do actions take only one argument? What if one were to pass extra parameters?
Just pass an object.
I once tried coming up with a description for the two types of actions when models were around:
- Static: like
set
which has the signaturepayload => model
- Dynamic: like
sum
which has the signaturepayload => model => model
Use static actions to insert new data directly into the model. Use dynamic actions when updating the existing model or performing other (potentially async) operations before calling another action.
Slightly off topic, I would love to see docs linked to a specific version using an existing well working system like Read the Docs. (example docs)
If any help is needed I'd be happy to help setting that up.
Would "principles" or "best practices" be an appropriate way to frame some notes or a collection of links to address stuff you should and shouldn't do?
For example, @kenOfYugen said "state mutation is bad". Ok. Why?
And what else should I know to be a good hyperapp developer?
In my first encounters with hyperapp, it occurs to me that I could probably make a mess for myself by putting the wrong things in the wrong places - putting stuff that should go in actions in state, etc.
@rhbecker state mutation is in general a potential source of errors. For hyperapp
check this out #501. Practically what you are saying is what my proposal was about.
For the time being, I suggest reading all the current documentation, and replicating the examples on your own. You' ll be fine! There is no such thing as a good hyperapp
developer. Because the API is minimal there is nothing new to learn, the complete opposite of e.g Angular. Just study JS from a functional language point of view, learn a new programming language and become a good programmer ๐!
I also keep grabbing the following code snippet for reference, would be good to see in the actions documentation:
const actions = {
setter: data => data,
getter: () => state => state,
reducer: () => state => state.count + 1,
notmutating: () => { ... },
async: () => (_, actions) => { actions.done() }
}
by @Swizz
@lukejacksonn Use static actions to insert new data directly into the model. Use dynamic actions when updating the existing model or performing other (potentially async) operations before calling another action.
I do think we need to document the two different action signatures a lot better. I'm not sure I agree with static vs dynamic for the naming since both take a dynamic data
argument. IMHO static would be either () => ({ ...newState })
or () => () => ({ ...newState })
where the results really are static and don't depend on any of the inputs.
We also need to make more than a single casual reference to state immutability in the actions section:
Every state update is immutable and produces a new object
I believe this should be super prominent and mentioned as part of state.
@kenOfYugen: Sorry if this was unclear from my last post. I wasn't literally asking why state mutation is bad. The point I was trying to make is that there are presumably some principles behind hyperapp - the arrangement of implementation into state, action, view, what ideally belongs in each of these, and what does not, etc.
So far, existing docs seem light on the why.
If I arrive here from some other framework or library that uses a different paradigm, and I don't understand the why of hyperapp, I might end up doing things I don't realize are incompatible with that why.
I am working on a couple of articles to improve the documentation ecosystem and maybe some of that will find its way into the official documentation as well. I was also thinking it would be nice to have a User's Guide, that teaches you only how to use Hyperapp without bogging down on details, like what is a VDOM, etc.
@rhbecker Hyperapp is probably one of the least intrusive "frameworks" out there, and this is not accidental but by design! ๐
Iโve always been unenthusiastic to learning a big framework. Not because they arenโt great, but on the fact that I like to know how things work "beneath the surface" and that I'm coding my own JavaScript, not the JavaScript \<insert framework here> wants me to use. The meat of it is I want to grow skills in JavaScript, not skills into frameworks.
Especially when that framework may be replaced in a few of years (if not months ๐ฎ). I want transferable skills, not obsolete skills.
I'm not certain how much of @JorgeBucaran's last reply was in response to what I've said. FWIW, I'm reading everything after mention of me as such.
It's great that hyperapp is "minimal", "simple", "non-intrusive", "light on opinion" and all the rest. You already do a great job marketing that aspect. It's what piqued my interest, and why I'm here trying to answer the original question of this thread.
But you know what's smaller than 1 KB? 0 KB. And what's even less opinionated? No opinion at all.
There's a reason to use hyperapp, instead of nothing, right? There's a reason you've chosen to arrange your applications into state, actions, and views. You whittled it down to the smallest amount of opinion you felt was necessary - so why is what is still there still there?
There's a reason @kenOfYugen said:
BTW, state mutation is bad, should it be allowed in the awesome list?
And that @JorgeBucaran followed that with:
They caught you @marcusasplund!
Are you expecting @marcusasplund to be the last hyperapp user to make such a mistake? Providing some insight for how to deal with state isn't intrusive. It helps users of hyperapp understand why they are bothering with hyperapp, and helps them use it more effectively - helps them get the most value out it.
The README says:
Hyperapp's design is inspired by The Elm Architecture. Create scalable browser-based applications using a functional paradigm.
Ok, but why? What about The Elm Architecture is commendable? Why should using a functional paradigm be appealing to me, as someone surveying my options?
It might be the case that you're expecting your audience to find you after already coming to the same realizations you did when you decided to make hyperapp. In that case, maybe the why is superfluous. But at least some folks are gonna happen upon hyperapp without understanding why any of this is good. In my opinion, the current state of the documentation does not get into that enough to satisfy that sort of audience.
Does that make any sense?
Are you expecting @marcusasplund to be the last hyperapp user to make such a mistake?
I was just playing with Marcus. Don't take it so seriously. ๐
In my opinion, the current state of the documentation does not get into that enough to satisfy that sort of audience.
IMO the current documentation is not very good, that's why I created this issue, and I appreciate your feedback.
Why should using a functional paradigm be appealing to me?
Telling you why FP is nice is out of the scope of Hyperapp's documentation. Talking about that is better suited for an opinionated blog post.
What about The Elm Architecture (TEA) is commendable?
Telling you what parts of TEA we embrace and what not would be a good idea. In the future, however, I want to stop marketing this so heavily, since we have diverged considerably from TEA. Right now I see Hyperapp as a "pragmatic TEA-inspired approach to state management + built-in VDOM engine".
But to answer your question, TEA inspired the very successful Redux as well as many similar spinoffs architectures, e.g., Vuex. Aso, from Elm's guide:
The Elm Architecture is a simple pattern for architecting webapps. It is great for modularity, code reuse, and testing. Ultimately, it makes it easy to create complex web apps that stay healthy as you refactor and add features.
Even so knowing the design decision is very interesting to know and can be educational when writing our own code using ha.
But I agree it should not be part of the core doc. Part of the extras or a linked blog post
@JorgeBucaran I came to Hyperapp after reading your post on SitePoint. It came at just the right time because I had been learning React and just been reading about Redux and why it was useful. The Redux site introduction did a good job about explaining why having a state object as a single source of truth and dispatching actions being the only way of updating that state. It made sense, but wiring Redux up to React seemed like quite a bit of work, especially for a simple app.
Then I read your blog post and saw a tiny JS library that seemed to do what React + Redux did in less code and a simple to use API. I'm not sure I would have appreciated what you had done if I hadn't read the Redux pages though.
Given this - I'd second what @rhbecker said - it would be nice to have something similar to the Redux pages for Hyperapp that explains the motivation behind the principles used by Hyperapp and some common use cases.
Havent read the whole thread, so don't know if it's been mentioned already, but: Actions are such a core concept of the API, there really should be a main section dedicated to them, like we have for Virtual DOM and Lifecycle events.
Hi, I would like to see more explanations about actions. Sometimes I don't understand how they work. For example with this action
const myaction = () => async state => //blahblah...
page does not refresh and nothing happens, no error, just nothing. I'm looking for a reason for an hour and then spontaneously remove async
keyword -- now it works. If you would have some more documentation and rules about implementationof actions it would help.
@algebraic-brain In the case of your async function, it returns a Promise
, and not a partial state object. Actions which return a Promise
do not trigger re-renders. or update the state (since Hyperapp doesn't support automatically merging what the Promise resolves). Here is a link to the relevant code in hyperapp
.
When you define an async action, you need to call regular, synchronous actions to update the app state & trigger re-draws. The "Gif Search" tutorial in the docs is a good example -- see the downloadGif
action.
https://github.com/hyperapp/hyperapp/blob/master/docs/tutorials/gif-search.md
Hope that clears things up for you! :smile:
@SkaterDad Thanks a lot, but my idea is that your explanation should be located directly in the documentation. Of course i've seen the "gif search" example but I was unable to deduce right understanding from that tutorial. Or i missed something?
@SkaterDad Thanks for explaining. @algebraic-brain I added it to the docs and will push this soon with a few other changes. I hope future arrivers will not go through this pain. Thank you, both!
The goal of this issue is to collect feedback about what stuff should be covered in the official documentation.