Skip to content

Simplify API surface. - #280

Merged
jorgebucaran merged 1 commit into
masterfrom
chore/emit
Jul 5, 2017
Merged

jorgebucaran merged 1 commit into
masterfrom
chore/emit

Conversation

@jorgebucaran

@jorgebucaran jorgebucaran commented Jul 4, 2017 •

Copy link
Copy Markdown
Collaborator
  • Don't pass appOptions to mixins.
  • Don't pass emit to events.
  • Don't pass emit to actions.
  • Update docs.
  • Pass emit as 2nd argument to mixins.

@jorgebucaran
jorgebucaran requested review from SkaterDad and zaceno July 4, 2017 13:46
@jorgebucaran jorgebucaran changed the title Don't pass emit to actions/events. Don't pass emit to events (also actions?). Jul 4, 2017
@jorgebucaran

Copy link
Copy Markdown
Collaborator Author

@zaceno There's a little issue when using emit inside the ready event. Since at that time app hasn't yet returned, emit is not available.

@codecov

codecov Bot commented Jul 4, 2017 •

Copy link
Copy Markdown

Codecov Report

Merging #280 into master will not change coverage.
The diff coverage is 100%.

Impacted file tree graph

@@          Coverage Diff          @@
##           master   #280   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           3      3           
  Lines         143    143           
  Branches       44     44           
=====================================
  Hits          143    143
Impacted Files Coverage Δ
src/app.js 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f7b2c16...a891113. Read the comment docs.

@Swizz

Swizz commented Jul 4, 2017 •

Copy link
Copy Markdown
Contributor

Stupid question but why ?

It is all about purity, but, if I dont want to use this pattern : (Yeah I know emit/event is not pure by meaning)

const emit = app({
  events: ...
  actions: ...
})

but directly

app({
  events: ...
  actions: ...
})

I wont be able to emit events ?


If I declare events in a file and actions in another one, where emit will come from ? Circular dependencies ?

@zaceno

zaceno commented Jul 4, 2017

Copy link
Copy Markdown
Contributor

@Swizz the solution would be to define your actions and events in one or more mixins. Since mixins get emit passed to them.

So there is a way. Wether you like that way or not, though... I can see how this might not be liked by everybody. (I like it though)

@zaceno

zaceno commented Jul 4, 2017

Copy link
Copy Markdown
Contributor

For me: getting emit passed to mixins is the best thing about this. Cleaning up the api by removing the emit parameter to actions and events would be nice, but if people are generally against it it may not be such a good idea.

@Swizz

Swizz commented Jul 4, 2017

Copy link
Copy Markdown
Contributor

I am not fundamentally against (not angry here), I was just raising questions in the case, in a genius bloom, nobody was thinking about it

@zaceno

zaceno commented Jul 4, 2017 •

Copy link
Copy Markdown
Contributor

@Swizz It is a good question to raise. Because it does mean that if you intend to compose together your app-options, and you need to emit custom events, you must use mixins. (Or do something really hacky. Not sure how, but there's probably a way)

I don't personally know of any valid reasons why you wouldn't compose your app using mixins. But maybe such reasons exist

@jorgebucaran

jorgebucaran commented Jul 4, 2017 •

Copy link
Copy Markdown
Collaborator Author

I think this is okay in preparation of a 1.0 release. If the world falls apart later, we can just add it again and it would not break anything, because:

download

Comment thread docs/api.md Outdated
@@ -91,10 +91,10 @@ Type: Array\<[Mixin](#mixins-mixin)\>
Type: ([props](#app-props)): [props](#mixin-props)

@zaceno zaceno Jul 4, 2017 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should read:

Type: ([props](#app-props), [emit](#emit)): [props](#mixin-props)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zaceno Done.

@zaceno zaceno left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to update the type signature of a mixin in the docs

@Swizz

Swizz commented Jul 4, 2017

Copy link
Copy Markdown
Contributor

From

import { app, h } from 'hyperapp'

const state = {
  count: 0
}

const actions = {
  inc: (state) => ({ count: state.count + 1 }),
  done: (_state, _actions, _data, emit) => emit('done')
}

const view = (state, actions) => {
  return h('button', { onclick: actions.inc }, state.count)
}

const emit = app({ root: document.getElementById('app'), state, view, actions })

to

import { app, h } from 'hyperapp'

const state =
  count: 0
}

const actions = (app, emit) => ({
  actions : {
    inc: (state) => ({ count: state.count + 1 }),
    done: () => emit('done')
  }
})

const view = (state, actions) => {
  return h('button', { onclick: actions.inc }, state.count)
}

const emit = app({ root: document.getElementById('app'), state, view, mixins: [actions] })

@jorgebucaran

Copy link
Copy Markdown
Collaborator Author

@Swizz emit is considered a lower level function. You rarely need it in your app. Why would you trigger an event if you can call an action? Now, when you do need it, it's probably better to hide that complexity in a mixin.

@zaceno

zaceno commented Jul 4, 2017

Copy link
Copy Markdown
Contributor

One of the primary use-cases I've found for emit, is actually when I'm trying to decouple my app into separate mixins.

Where sometimes in mixin A, I'll need to call an action in mixin B. Now I may know the path to the action in mixin B. But it's not good practice to hardcode that sort of logic into mixin B. I'd like to be able to change mixin A without breaking mixin B.

So I'll use events for that. Rather than call the action in mixin B, I'll just emit an event, and have mixin A listen for it.

@Swizz

Swizz commented Jul 4, 2017

Copy link
Copy Markdown
Contributor

I will need to change my mind about events tho.
In a Vuex way, I was using actions as Mutators (things that change the state or call events) and events as Actions (things will do not pure stuffs).

@jorgebucaran

Copy link
Copy Markdown
Collaborator Author

By the way, about passing the app into mixins. Other than the view is useless information because the state is the global state before other mixins have loaded, the actions are not yet initialized/wrapped.

@zaceno

zaceno commented Jul 4, 2017

Copy link
Copy Markdown
Contributor

@jbucaran I was just looking at that yesterday. Why do we do that? What's the envisioned use case? (Maybe that's for a slack-discussion, but anyway)

@jorgebucaran

Copy link
Copy Markdown
Collaborator Author

@Swizz If you were using events in that way and this PR causes you to realize that was not the hyperapp-way, then I think we succeeded. While you can continue to do so at a certain extent with mixins, you should be using actions both as a reducers/mutators and side effects.

@jorgebucaran

Copy link
Copy Markdown
Collaborator Author

@zaceno Why do we do that?

Because I didn't know what else to pass. Shall we knock it off too?

@zaceno

zaceno commented Jul 4, 2017

Copy link
Copy Markdown
Contributor

@Swizz: back in the day we used to have "reducers" = vuex mutators, and "effects" = vuex actions. Then we merged them together into hyperapp's actions which mostly resemble vuex mutators, but are able to call other actions, and do async stuff, so a little like vuex effects at the same time.

@zaceno

zaceno commented Jul 4, 2017

Copy link
Copy Markdown
Contributor

@jbucaran: why not, if there's no reason for it. It would certainly help the sinister alchemy I've got cooking.

@jorgebucaran

Copy link
Copy Markdown
Collaborator Author

@Swizz With regards to that reason, it was a tradeoff (to me). By making actions both mutators/effects we were not really changing how anything worked internally, but it made the API much cleaner and easier to learn.

@jorgebucaran jorgebucaran changed the title Don't pass emit to events (also actions?). Simplify API surface. Jul 4, 2017
@jorgebucaran

Copy link
Copy Markdown
Collaborator Author

@zaceno I've updated this PR so we don't pass the app to mixins either.

I also checked all the mixins I know in existence, ones I've made and ones that have been published as I was made aware of and none were using appOptions. Actually, the only one was the router, which I'll make sure to update after this is merged.

@jorgebucaran

Copy link
Copy Markdown
Collaborator Author

@matejmazur @jamen @andyrj have all authored mixins, so this PR is of your interest. Please have a look when you have a chance. 🙏

@andyrj

andyrj commented Jul 4, 2017

Copy link
Copy Markdown
Contributor

I don't believe I utilized emit in my mixins, so as long as actions and events are still going to be available inside a mixin I am ok with this change and can update how actions are initialized. I like the cleaner api this provides.

@jamen

jamen commented Jul 4, 2017

Copy link
Copy Markdown

These changes seem okay to me. I haven't personally used appOptions or emitin my mixins yet, so it doesn't affect me. But, I wasn't really a fan of appOptions to begin with.

@MatejBransky

Copy link
Copy Markdown

I'm ok with it.

@SkaterDad

Copy link
Copy Markdown
Contributor

Forcing people to use mixins when they want custom events seemed like a strange idea at first, but I'm warming up to it. Restricting how people use parts of the framework is certainly in line with the Elm philosophy.

It is nice that you can emit events directly from the view functions with this change.

The only downside is the way to use emit at the main app-level actions now is really awkward in my opinion. It almost looks like it shouldn't work 😆

const emit = app({
  actions: {
    alldone:  () => emit('done')
  }
})

@jorgebucaran

Copy link
Copy Markdown
Collaborator Author

@SkaterDad It will work in actions because they will run after the app() call has returned for sure.

It will not work for events.ready though, since that one runs before app returns.

- Don't pass emit to actions/events.
- Don't pass appOptions to mixins.
@jorgebucaran
jorgebucaran merged commit f2e768f into master Jul 5, 2017
@jorgebucaran
jorgebucaran deleted the chore/emit branch July 5, 2017 12:14
@zaceno

zaceno commented Jul 5, 2017

Copy link
Copy Markdown
Contributor

🎉

jorgebucaran added a commit that referenced this pull request Jan 7, 2018
- Don't pass emit to actions/events.
- Don't pass appOptions to mixins.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants